diff --git a/mps/.p4ignore b/mps/.p4ignore index 25cd419ae6d..89c70e46dc5 100644 --- a/mps/.p4ignore +++ b/mps/.p4ignore @@ -8,16 +8,25 @@ # Patch results *.orig *.rej +# Autoconf and Automake output Makefile autom4te.cache config.log config.status +.deps +.dirstamp +bin +lib +# Misc TAGS *.dSYM -code/*/*/*.d *.pyc test/obj test/test/log test/test/obj ....gcda -....gcno \ No newline at end of file +....gcno +\#*# +*~ +.#.* +core diff --git a/mps/.renamed-gitignore b/mps/.renamed-gitignore new file mode 120000 index 00000000000..c5c99a6a89c --- /dev/null +++ b/mps/.renamed-gitignore @@ -0,0 +1 @@ +.p4ignore \ No newline at end of file diff --git a/mps/.travis.yml b/mps/.travis.yml index d78b45e190a..049d3b8848a 100644 --- a/mps/.travis.yml +++ b/mps/.travis.yml @@ -16,5 +16,9 @@ notifications: email: - mps-travis@ravenbrook.com irc: "irc.freenode.net#memorypoolsystem" +# This shows how you can ask Travis to install or update packages. +#before_install: +# - if test "$TRAVIS_OS_NAME" = "linux"; then sudo apt-get -qq update; fi +# - if test "$TRAVIS_OS_NAME" = "linux"; then sudo apt-get install -y gcc-4.7; fi script: - ./configure --prefix=$PWD/prefix && make install && make test diff --git a/mps/Makefile.in b/mps/Makefile.in index fe33a4d49f6..cdefe0890ea 100644 --- a/mps/Makefile.in +++ b/mps/Makefile.in @@ -1,7 +1,7 @@ # Makefile.in -- source for autoconf Makefile # # $Id$ -# Copyright (C) 2012-2014 Ravenbrook Limited. See end of file for license. +# Copyright (C) 2012-2016 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. @@ -71,7 +71,7 @@ make-install-dirs: install: @INSTALL_TARGET@ test-make-build: - $(MAKE) $(TARGET_OPTS) testci + $(MAKE) $(TARGET_OPTS) testci testratio testscheme $(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 @@ -80,3 +80,44 @@ test-xcode-build: $(XCODEBUILD) -config Release -target testci test: @TEST_TARGET@ + + +# C. COPYRIGHT AND LICENSE +# +# Copyright (C) 2012-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: +# +# 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/mps/code/.p4ignore b/mps/code/.p4ignore index 7db90ebb062..70e69009357 100644 --- a/mps/code/.p4ignore +++ b/mps/code/.p4ignore @@ -14,6 +14,8 @@ lii6ll w3i3mv w3i6mv xci3gc +xci3ll +xci6gc xci6ll # Visual Studio junk Debug @@ -33,6 +35,7 @@ mpsio*.txt *.lib *.exe a.out +core # Xcode junk xc mps.xcodeproj/xcuserdata @@ -53,3 +56,5 @@ tags .DS_Store # Emacs backups *~ +# GNU make dependencies +*/*/*.d diff --git a/mps/code/.renamed-gitignore b/mps/code/.renamed-gitignore new file mode 120000 index 00000000000..c5c99a6a89c --- /dev/null +++ b/mps/code/.renamed-gitignore @@ -0,0 +1 @@ +.p4ignore \ No newline at end of file diff --git a/mps/code/abq.c b/mps/code/abq.c index 0e9b808a277..b88914e3c17 100644 --- a/mps/code/abq.c +++ b/mps/code/abq.c @@ -1,7 +1,7 @@ /* abq.c: QUEUE IMPLEMENTATION * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * * .purpose: A fixed-length FIFO queue. * @@ -41,8 +41,7 @@ Res ABQInit(Arena arena, ABQ abq, void *owner, Count elements, Size elementSize) "empty" from "full" */ elements = elements + 1; - res = ControlAlloc(&p, arena, ABQQueueSize(elements, elementSize), - /* withReservoirPermit */ FALSE); + res = ControlAlloc(&p, arena, ABQQueueSize(elements, elementSize)); if (res != ResOK) return res; @@ -232,7 +231,7 @@ Count ABQDepth(ABQ abq) /* ABQIterate -- call 'visitor' for each element in an ABQ */ -void ABQIterate(ABQ abq, ABQVisitor visitor, void *closureP, Size closureS) +void ABQIterate(ABQ abq, ABQVisitor visitor, void *closure) { Index copy, index, in; @@ -247,7 +246,7 @@ void ABQIterate(ABQ abq, ABQVisitor visitor, void *closureP, Size closureS) void *element = ABQElement(abq, index); Bool delete = FALSE; Bool cont; - cont = (*visitor)(&delete, element, closureP, closureS); + cont = (*visitor)(&delete, element, closure); AVERT(Bool, cont); AVERT(Bool, delete); if (!delete) { @@ -295,14 +294,15 @@ static Index ABQNextIndex(ABQ abq, Index index) /* ABQElement -- return pointer to the index'th element in the queue vector. */ -static void *ABQElement(ABQ abq, Index index) { +static void *ABQElement(ABQ abq, Index index) +{ return PointerAdd(abq->queue, index * abq->elementSize); } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/abq.h b/mps/code/abq.h index 85cdfcd5756..90591de9a5f 100644 --- a/mps/code/abq.h +++ b/mps/code/abq.h @@ -24,7 +24,7 @@ typedef struct ABQStruct *ABQ; typedef Res (*ABQDescribeElement)(void *element, mps_lib_FILE *stream, Count depth); -typedef Bool (*ABQVisitor)(Bool *deleteReturn, void *element, void *closureP, Size closureS); +typedef Bool (*ABQVisitor)(Bool *deleteReturn, void *element, void *closure); extern Res ABQInit(Arena arena, ABQ abq, void *owner, Count elements, Size elementSize); extern Bool ABQCheck(ABQ abq); @@ -36,7 +36,7 @@ extern Res ABQDescribe(ABQ abq, ABQDescribeElement describeElement, mps_lib_FILE extern Bool ABQIsEmpty(ABQ abq); extern Bool ABQIsFull(ABQ abq); extern Count ABQDepth(ABQ abq); -extern void ABQIterate(ABQ abq, ABQVisitor visitor, void *closureP, Size closureS); +extern void ABQIterate(ABQ abq, ABQVisitor visitor, void *closure); /* Types */ @@ -50,10 +50,10 @@ typedef struct ABQStruct void *queue; /* Meter queue depth at each operation */ - METER_DECL(push); - METER_DECL(pop); - METER_DECL(peek); - METER_DECL(delete); + METER_DECL(push) + METER_DECL(pop) + METER_DECL(peek) + METER_DECL(delete) Sig sig; } ABQStruct; diff --git a/mps/code/abqtest.c b/mps/code/abqtest.c index 9aad3351cb6..59f2a64fcd4 100644 --- a/mps/code/abqtest.c +++ b/mps/code/abqtest.c @@ -1,7 +1,7 @@ /* abqtest.c: AVAILABLE BLOCK QUEUE TEST * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. */ #include "abq.h" @@ -92,12 +92,10 @@ typedef struct TestClosureStruct { } TestClosureStruct; static Bool TestDeleteCallback(Bool *deleteReturn, void *element, - void *closureP, Size closureS) + void *closure) { TestBlock *a = (TestBlock *)element; - TestClosure cl = (TestClosure)closureP; - AVER(closureS == UNUSED_SIZE); - UNUSED(closureS); + TestClosure cl = (TestClosure)closure; if (*a == cl->b) { *deleteReturn = TRUE; cl->res = ResOK; @@ -145,13 +143,13 @@ static void step(void) cdie(b != NULL, "found to delete"); cl.b = b; cl.res = ResFAIL; - ABQIterate(&abq, TestDeleteCallback, &cl, UNUSED_SIZE); + ABQIterate(&abq, TestDeleteCallback, &cl); cdie(cl.res == ResOK, "ABQIterate"); } } } -extern int main(int argc, char *argv[]) +int main(int argc, char *argv[]) { mps_arena_t arena; int i; @@ -184,7 +182,7 @@ extern int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2014 Ravenbrook Limited . + * Copyright (c) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/airtest.c b/mps/code/airtest.c index ad0ec456b99..8b5a6ac8a5c 100644 --- a/mps/code/airtest.c +++ b/mps/code/airtest.c @@ -1,7 +1,7 @@ /* 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. + * $Id$ + * Copyright (c) 2014-2016 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 @@ -163,7 +163,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2014 Ravenbrook Limited . + * Copyright (c) 2014-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/amcssth.c b/mps/code/amcssth.c index 596d818bc0b..5b15a5535e9 100644 --- a/mps/code/amcssth.c +++ b/mps/code/amcssth.c @@ -1,21 +1,13 @@ /* amcssth.c: POOL CLASS AMC STRESS TEST WITH TWO THREADS * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. * Portions copyright (c) 2002 Global Graphics Software. * - * .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. + * 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. */ #include "fmtdy.h" @@ -28,11 +20,6 @@ #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. */ #define testArenaSIZE ((size_t)1000*1024) @@ -133,13 +120,17 @@ typedef struct closure_s { static void *kid_thread(void *arg) { void *marker = ▮ - mps_thr_t thread; + mps_thr_t thread1, thread2; 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_thread(®_root, arena, thread, marker), + /* Register the thread twice to check this is supported -- see + * + */ + die(mps_thread_reg(&thread1, arena), "thread_reg"); + die(mps_thread_reg(&thread2, arena), "thread_reg"); + die(mps_root_create_thread(®_root, arena, thread1, marker), "root_create"); die(mps_ap_create(&ap, cl->pool, mps_rank_exact()), "BufferCreate(fooey)"); @@ -149,7 +140,8 @@ static void *kid_thread(void *arg) mps_ap_destroy(ap); mps_root_destroy(reg_root); - mps_thread_dereg(thread); + mps_thread_dereg(thread2); + mps_thread_dereg(thread1); return NULL; } @@ -157,8 +149,7 @@ static void *kid_thread(void *arg) /* test -- the body of the test */ -static void test_pool(const char *name, mps_pool_t pool, size_t roots_count, - int mode) +static void test_pool(const char *name, mps_pool_t pool, size_t roots_count) { size_t i; mps_word_t rampSwitch; @@ -170,8 +161,7 @@ static void test_pool(const char *name, mps_pool_t pool, size_t roots_count, closure_s cl; int walked = FALSE, ramped = FALSE; - printf("\n------ mode: %s pool: %s-------\n", - mode == ModeWALK ? "WALK" : "COMMIT", name); + printf("\n------ pool: %s-------\n", name); cl.pool = pool; cl.roots_count = roots_count; @@ -203,7 +193,7 @@ static void test_pool(const char *name, mps_pool_t pool, size_t roots_count, 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("\nCollection %lu finished:\n", (unsigned long)collections++); printf("live %"PRIuLONGEST"\n", (ulongest_t)live); printf("condemned %"PRIuLONGEST"\n", (ulongest_t)condemned); printf("not_condemned %"PRIuLONGEST"\n", (ulongest_t)not_condemned); @@ -217,7 +207,7 @@ static void test_pool(const char *name, mps_pool_t pool, size_t roots_count, cdie(exactRoots[i] == objNULL || dylan_check(exactRoots[i]), "all roots check"); - if (mode == ModeWALK && collections >= collectionsCOUNT / 2 && !walked) + if (collections >= collectionsCOUNT / 2 && !walked) { unsigned long count = 0; mps_arena_park(arena); @@ -278,7 +268,7 @@ static void test_pool(const char *name, mps_pool_t pool, size_t roots_count, testthr_join(&kids[i], NULL); } -static void test_arena(int mode) +static void test_arena(void) { size_t i; mps_fmt_t format; @@ -291,8 +281,6 @@ static void test_arena(int mode) 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()); @@ -324,8 +312,8 @@ static void test_arena(int mode) die(mps_pool_create(&amcz_pool, arena, mps_class_amcz(), format, chain), "pool_create(amcz)"); - test_pool("AMC", amc_pool, exactRootsCOUNT, mode); - test_pool("AMCZ", amcz_pool, 0, mode); + test_pool("AMC", amc_pool, exactRootsCOUNT); + test_pool("AMCZ", amcz_pool, 0); mps_arena_park(arena); mps_pool_destroy(amc_pool); @@ -342,8 +330,7 @@ static void test_arena(int mode) int main(int argc, char *argv[]) { testlib_init(argc, argv); - test_arena(ModeWALK); - test_arena(ModeCOMMIT); + test_arena(); printf("%s: Conclusion: Failed to find any defects.\n", argv[0]); return 0; @@ -352,7 +339,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2014 Ravenbrook Limited . + * Copyright (c) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/amsss.c b/mps/code/amsss.c index 8083123f482..38fa7457d17 100644 --- a/mps/code/amsss.c +++ b/mps/code/amsss.c @@ -1,7 +1,7 @@ /* amsss.c: POOL CLASS AMS STRESS TEST * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 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 @@ -76,8 +76,6 @@ static void report(void) mps_message_discard(arena, message); } - - return; } @@ -249,7 +247,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2014 Ravenbrook Limited . + * Copyright (c) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/anangc.gmk b/mps/code/anangc.gmk index f0a7d2ff515..1c5fb380e16 100644 --- a/mps/code/anangc.gmk +++ b/mps/code/anangc.gmk @@ -10,9 +10,9 @@ PFM = anangc MPMPF = \ lockan.c \ prmcan.c \ + prmcanan.c \ protan.c \ span.c \ - ssan.c \ than.c \ vman.c diff --git a/mps/code/ananll.gmk b/mps/code/ananll.gmk index cc95645f212..849adffa8b6 100644 --- a/mps/code/ananll.gmk +++ b/mps/code/ananll.gmk @@ -10,9 +10,9 @@ PFM = ananll MPMPF = \ lockan.c \ prmcan.c \ + prmcanan.c \ protan.c \ span.c \ - ssan.c \ than.c \ vman.c diff --git a/mps/code/ananmv.nmk b/mps/code/ananmv.nmk index 41d80a0671a..504140a772c 100644 --- a/mps/code/ananmv.nmk +++ b/mps/code/ananmv.nmk @@ -1,7 +1,7 @@ # ananmv.nmk: ANSI/ANSI/MICROSOFT VISUAL C/C++ NMAKE FILE -*- makefile -*- # # $Id$ -# Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. PFM = ananmv @@ -10,9 +10,9 @@ PFMDEFS = /DCONFIG_PF_ANSI /DCONFIG_THREAD_SINGLE MPMPF = \ [lockan] \ [prmcan] \ + [prmcanan] \ [protan] \ [span] \ - [ssan] \ [than] \ [vman] @@ -23,7 +23,7 @@ MPMPF = \ # C. COPYRIGHT AND LICENSE # -# Copyright (C) 2001-2014 Ravenbrook Limited . +# Copyright (C) 2001-2018 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. # diff --git a/mps/code/apss.c b/mps/code/apss.c index 5192baada42..05890ed2605 100644 --- a/mps/code/apss.c +++ b/mps/code/apss.c @@ -1,12 +1,11 @@ /* apss.c: AP MANUAL ALLOC STRESS TEST * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. */ -#include "mpscmv.h" #include "mpscmvff.h" #include "mpscmvt.h" #include "mpslib.h" @@ -23,6 +22,7 @@ #define testArenaSIZE ((((size_t)3)<<24) - 4) #define testSetSIZE 200 #define testLOOPS 10 +#define MAX_ALIGN 64 /* TODO: Make this test work up to arena_grain_size? */ /* make -- allocate one object */ @@ -76,11 +76,12 @@ static mps_res_t stress(mps_arena_t arena, mps_pool_debug_option_s *options, /* allocate a load of objects */ for (i=0; i= sizeof(ps[i])) *ps[i] = 1; /* Write something, so it gets swap. */ @@ -120,10 +121,12 @@ static mps_res_t stress(mps_arena_t arena, mps_pool_debug_option_s *options, } /* allocate some new objects */ for (i=testSetSIZE/2; i. + * Copyright (c) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/arena.c b/mps/code/arena.c index 9ea95d57822..8702b290a72 100644 --- a/mps/code/arena.c +++ b/mps/code/arena.c @@ -1,12 +1,12 @@ /* arena.c: ARENA ALLOCATION FEATURES * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * * .sources: is the main design document. */ #include "tract.h" -#include "poolmv.h" +#include "poolmvff.h" #include "mpm.h" #include "cbs.h" #include "bt.h" @@ -17,7 +17,7 @@ SRCID(arena, "$Id$"); -#define ArenaControlPool(arena) MVPool(&(arena)->controlPoolStruct) +#define ArenaControlPool(arena) MVFFPool(&(arena)->controlPoolStruct) #define ArenaCBSBlockPool(arena) MFSPool(&(arena)->freeCBSBlockPoolStruct) #define ArenaFreeLand(arena) CBSLand(&(arena)->freeLandStruct) @@ -42,87 +42,130 @@ Bool ArenaGrainSizeCheck(Size size) static void ArenaTrivCompact(Arena arena, Trace trace); static void arenaFreePage(Arena arena, Addr base, Pool pool); static void arenaFreeLandFinish(Arena arena); +static Res ArenaAbsInit(Arena arena, Size grainSize, ArgList args); +static void ArenaAbsFinish(Inst inst); +static Res ArenaAbsDescribe(Inst inst, mps_lib_FILE *stream, Count depth); -/* ArenaTrivDescribe -- produce trivial description of an arena */ - -static Res ArenaTrivDescribe(Arena arena, mps_lib_FILE *stream, Count depth) +static void ArenaNoFree(Addr base, Size size, Pool pool) { - if (!TESTT(Arena, arena)) - return ResFAIL; - if (stream == NULL) - return ResFAIL; + UNUSED(base); + UNUSED(size); + UNUSED(pool); + NOTREACHED; +} - /* .describe.triv.never-called-from-subclass-method: - * This Triv method seems to assume that it will never get called - * from a subclass-method invoking ARENA_SUPERCLASS()->describe. - * It assumes that it only gets called if the describe method has - * not been subclassed. (That's the only reason for printing the - * "No class-specific description available" message). - * This is bogus, but that's the status quo. RHSK 2007-04-27. - */ - /* .describe.triv.dont-upcall: Therefore (for now) the last - * subclass describe method should avoid invoking - * ARENA_SUPERCLASS()->describe. RHSK 2007-04-27. - */ - return WriteF(stream, depth, - " No class-specific description available.\n", NULL); +static Res ArenaNoChunkInit(Chunk chunk, BootBlock boot) +{ + UNUSED(chunk); + UNUSED(boot); + NOTREACHED; + return ResUNIMPL; +} + +static void ArenaNoChunkFinish(Chunk chunk) +{ + UNUSED(chunk); + NOTREACHED; +} + +static Res ArenaNoPagesMarkAllocated(Arena arena, Chunk chunk, + Index baseIndex, Count pages, + Pool pool) +{ + UNUSED(arena); + UNUSED(chunk); + UNUSED(baseIndex); + UNUSED(pages); + UNUSED(pool); + NOTREACHED; + return ResUNIMPL; +} + +static Bool ArenaNoChunkPageMapped(Chunk chunk, Index index) +{ + UNUSED(chunk); + UNUSED(index); + NOTREACHED; + return FALSE; +} + +static Res ArenaNoCreate(Arena *arenaReturn, ArgList args) +{ + UNUSED(arenaReturn); + UNUSED(args); + NOTREACHED; + return ResUNIMPL; +} + +static void ArenaNoDestroy(Arena arena) +{ + UNUSED(arena); + NOTREACHED; +} + +DEFINE_CLASS(Inst, ArenaClass, klass) +{ + INHERIT_CLASS(klass, ArenaClass, InstClass); + AVERT(InstClass, klass); } -/* AbstractArenaClass -- The abstract arena class definition - * - * .null: Most abstract class methods are set to NULL. See - * . */ +/* AbstractArenaClass -- The abstract arena class definition */ -typedef ArenaClassStruct AbstractArenaClassStruct; - -DEFINE_CLASS(AbstractArenaClass, class) +DEFINE_CLASS(Arena, AbstractArena, klass) { - INHERIT_CLASS(&class->protocol, ProtocolClass); - class->name = "ABSARENA"; - class->size = 0; - class->offset = 0; - class->varargs = ArgTrivVarargs; - class->init = NULL; - class->finish = NULL; - class->purgeSpare = ArenaNoPurgeSpare; - class->extend = ArenaNoExtend; - class->grow = ArenaNoGrow; - class->free = NULL; - class->chunkInit = NULL; - class->chunkFinish = NULL; - class->compact = ArenaTrivCompact; - class->describe = ArenaTrivDescribe; - class->pagesMarkAllocated = NULL; - class->sig = ArenaClassSig; + INHERIT_CLASS(&klass->instClassStruct, AbstractArena, Inst); + klass->instClassStruct.finish = ArenaAbsFinish; + klass->instClassStruct.describe = ArenaAbsDescribe; + klass->size = sizeof(ArenaStruct); + klass->varargs = ArgTrivVarargs; + klass->init = ArenaAbsInit; + klass->create = ArenaNoCreate; + klass->destroy = ArenaNoDestroy; + klass->purgeSpare = ArenaNoPurgeSpare; + klass->extend = ArenaNoExtend; + klass->grow = ArenaNoGrow; + klass->free = ArenaNoFree; + klass->chunkInit = ArenaNoChunkInit; + klass->chunkFinish = ArenaNoChunkFinish; + klass->compact = ArenaTrivCompact; + klass->pagesMarkAllocated = ArenaNoPagesMarkAllocated; + klass->chunkPageMapped = ArenaNoChunkPageMapped; + klass->sig = ArenaClassSig; + AVERT(ArenaClass, klass); } /* ArenaClassCheck -- check the consistency of an arena class */ -Bool ArenaClassCheck(ArenaClass class) +Bool ArenaClassCheck(ArenaClass klass) { - 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 */ - /* greater than the size of the class-specific portion of the */ - /* instance. */ - CHECKL(class->offset <= (size_t)(class->size - sizeof(ArenaStruct))); - CHECKL(FUNCHECK(class->varargs)); - CHECKL(FUNCHECK(class->init)); - CHECKL(FUNCHECK(class->finish)); - CHECKL(FUNCHECK(class->purgeSpare)); - CHECKL(FUNCHECK(class->extend)); - 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); + CHECKD(InstClass, &klass->instClassStruct); + CHECKL(klass->size >= sizeof(ArenaStruct)); + CHECKL(FUNCHECK(klass->varargs)); + CHECKL(FUNCHECK(klass->init)); + CHECKL(FUNCHECK(klass->create)); + CHECKL(FUNCHECK(klass->destroy)); + CHECKL(FUNCHECK(klass->purgeSpare)); + CHECKL(FUNCHECK(klass->extend)); + CHECKL(FUNCHECK(klass->grow)); + CHECKL(FUNCHECK(klass->free)); + CHECKL(FUNCHECK(klass->chunkInit)); + CHECKL(FUNCHECK(klass->chunkFinish)); + CHECKL(FUNCHECK(klass->compact)); + CHECKL(FUNCHECK(klass->pagesMarkAllocated)); + CHECKL(FUNCHECK(klass->chunkPageMapped)); + + /* Check that arena classes override sets of related methods. */ + CHECKL((klass->init == ArenaAbsInit) + == (klass->instClassStruct.finish == ArenaAbsFinish)); + CHECKL((klass->create == ArenaNoCreate) + == (klass->destroy == ArenaNoDestroy)); + CHECKL((klass->chunkInit == ArenaNoChunkInit) + == (klass->chunkFinish == ArenaNoChunkFinish)); + + CHECKS(ArenaClass, klass); return TRUE; } @@ -131,14 +174,12 @@ Bool ArenaClassCheck(ArenaClass class) Bool ArenaCheck(Arena arena) { - CHECKS(Arena, arena); + CHECKC(AbstractArena, arena); CHECKD(Globals, ArenaGlobals(arena)); - CHECKD(ArenaClass, arena->class); CHECKL(BoolCheck(arena->poolReady)); if (arena->poolReady) { /* */ - CHECKD(MV, &arena->controlPoolStruct); - CHECKD(Reservoir, &arena->reservoirStruct); + CHECKD(MVFF, &arena->controlPoolStruct); } /* .reserved.check: Would like to check that arena->committed <= @@ -149,12 +190,15 @@ Bool ArenaCheck(Arena arena) */ CHECKL(arena->committed <= arena->commitLimit); CHECKL(arena->spareCommitted <= arena->committed); + CHECKL(0.0 <= arena->pauseTime); - CHECKL(ShiftCheck(arena->zoneShift)); + CHECKL(arena->zoneShift == ZoneShiftUNSET + || ShiftCheck(arena->zoneShift)); CHECKL(ArenaGrainSizeCheck(arena->grainSize)); /* Stripes can't be smaller than grains. */ - CHECKL(((Size)1 << arena->zoneShift) >= arena->grainSize); + CHECKL(arena->zoneShift == ZoneShiftUNSET + || ((Size)1 << arena->zoneShift) >= arena->grainSize); if (arena->lastTract == NULL) { CHECKL(arena->lastTractBase == (Addr)0); @@ -165,7 +209,7 @@ Bool ArenaCheck(Arena arena) if (arena->primary != NULL) { CHECKD(Chunk, arena->primary); } - CHECKD_NOSIG(Ring, &arena->chunkRing); + CHECKD_NOSIG(Ring, ArenaChunkRing(arena)); /* Can't use CHECKD_NOSIG because TreeEMPTY is NULL. */ CHECKL(TreeCheck(ArenaChunkTree(arena))); /* TODO: check that the chunkRing and chunkTree have identical members */ @@ -183,26 +227,18 @@ Bool ArenaCheck(Arena arena) } -/* ArenaInit -- initialize the generic part of the arena - * - * .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. - */ +/* ArenaAbsInit -- initialize the generic part of the arena */ -Res ArenaInit(Arena arena, ArenaClass class, Size grainSize, ArgList args) +static Res ArenaAbsInit(Arena arena, Size grainSize, ArgList args) { Res res; Bool zoned = ARENA_DEFAULT_ZONED; Size commitLimit = ARENA_DEFAULT_COMMIT_LIMIT; double spare = ARENA_SPARE_DEFAULT; + double pauseTime = ARENA_DEFAULT_PAUSE_TIME; mps_arg_s arg; AVER(arena != NULL); - AVERT(ArenaClass, class); AVERT(ArenaGrainSize, grainSize); if (ArgPick(&arg, args, MPS_KEY_ARENA_ZONED)) @@ -214,17 +250,21 @@ Res ArenaInit(Arena arena, ArenaClass class, Size grainSize, ArgList args) /* MPS_KEY_SPARE_COMMIT_LIMIT is deprecated */ if (ArgPick(&arg, args, MPS_KEY_SPARE_COMMIT_LIMIT)) spare = (double)arg.val.size / (double)commitLimit; + if (ArgPick(&arg, args, MPS_KEY_PAUSE_TIME)) + pauseTime = arg.val.d; - arena->class = class; + /* Superclass init */ + InstInit(CouldBeA(Inst, arena)); arena->reserved = (Size)0; arena->committed = (Size)0; arena->commitLimit = commitLimit; arena->spareCommitted = (Size)0; arena->spare = spare; + arena->pauseTime = pauseTime; arena->grainSize = grainSize; - /* zoneShift is usually overridden by init */ - arena->zoneShift = ARENA_ZONESHIFT; + /* zoneShift must be overridden by arena class init */ + arena->zoneShift = ZoneShiftUNSET; arena->poolReady = FALSE; /* */ arena->lastTract = NULL; arena->lastTractBase = NULL; @@ -233,7 +273,7 @@ Res ArenaInit(Arena arena, ArenaClass class, Size grainSize, ArgList args) arena->zoned = zoned; arena->primary = NULL; - RingInit(&arena->chunkRing); + RingInit(ArenaChunkRing(arena)); arena->chunkTree = TreeEMPTY; arena->chunkSerial = (Serial)0; @@ -243,8 +283,9 @@ Res ArenaInit(Arena arena, ArenaClass class, Size grainSize, ArgList args) if (res != ResOK) goto failGlobalsInit; + SetClassOfPoly(arena, CLASS(AbstractArena)); arena->sig = ArenaSig; - AVERT(Arena, arena); + AVERC(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 @@ -262,19 +303,12 @@ Res ArenaInit(Arena arena, ArenaClass class, Size grainSize, ArgList args) if (res != ResOK) goto failMFSInit; - /* initialize the reservoir, */ - res = ReservoirInit(&arena->reservoirStruct, arena); - if (res != ResOK) - goto failReservoirInit; - - AVERT(Arena, arena); return ResOK; -failReservoirInit: - PoolFinish(ArenaCBSBlockPool(arena)); failMFSInit: GlobalsFinish(ArenaGlobals(arena)); failGlobalsInit: + InstFinish(MustBeA(Inst, arena)); return res; } @@ -297,6 +331,7 @@ 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); +ARG_DEFINE_KEY(PAUSE_TIME, double); static Res arenaFreeLandInit(Arena arena) { @@ -309,7 +344,7 @@ static Res arenaFreeLandInit(Arena arena) /* Initialise the free land. */ MPS_ARGS_BEGIN(liArgs) { MPS_ARGS_ADD(liArgs, CBSBlockPool, ArenaCBSBlockPool(arena)); - res = LandInit(ArenaFreeLand(arena), CBSZonedLandClassGet(), arena, + res = LandInit(ArenaFreeLand(arena), CLASS(CBSZoned), arena, ArenaGrainSize(arena), arena, liArgs); } MPS_ARGS_END(liArgs); AVER(res == ResOK); /* no allocation, no failure expected */ @@ -335,13 +370,13 @@ static Res arenaFreeLandInit(Arena arena) return res; } -Res ArenaCreate(Arena *arenaReturn, ArenaClass class, ArgList args) +Res ArenaCreate(Arena *arenaReturn, ArenaClass klass, ArgList args) { Arena arena; Res res; AVER(arenaReturn != NULL); - AVERT(ArenaClass, class); + AVERT(ArenaClass, klass); AVERT(ArgList, args); /* We must initialise the event subsystem very early, because event logging @@ -349,12 +384,17 @@ Res ArenaCreate(Arena *arenaReturn, ArenaClass class, ArgList args) to the EventLast pointers. */ EventInit(); - /* Do initialization. This will call ArenaInit (see .init.caller). */ - res = (*class->init)(&arena, class, args); + res = klass->create(&arena, args); if (res != ResOK) goto failInit; - /* Grain size must have been set up by *class->init() */ + /* Zone shift must have been set up by klass->create() */ + AVER(ShiftCheck(arena->zoneShift)); + + /* TODO: Consider how each of the stages below could be incorporated + into arena initialization, rather than tacked on here. */ + + /* Grain size must have been set up by klass->create() */ if (ArenaGrainSize(arena) > ((Size)1 << arena->zoneShift)) { res = ResMEMORY; /* size was too small */ goto failStripeSize; @@ -382,26 +422,24 @@ Res ArenaCreate(Arena *arenaReturn, ArenaClass class, ArgList args) arenaFreeLandFinish(arena); failFreeLandInit: failStripeSize: - (*class->finish)(arena); + klass->destroy(arena); failInit: return res; } -/* ArenaFinish -- finish the generic part of the arena - * - * .finish.caller: Unlike PoolFinish, this is called by the class finish - * methods, not the generic Destroy. This is because the class is - * responsible for deallocating the descriptor. */ +/* ArenaAbsFinish -- finish the generic part of the arena */ -void ArenaFinish(Arena arena) +static void ArenaAbsFinish(Inst inst) { + Arena arena = MustBeA(AbstractArena, inst); + AVERC(Arena, arena); PoolFinish(ArenaCBSBlockPool(arena)); - ReservoirFinish(ArenaReservoir(arena)); arena->sig = SigInvalid; + NextMethod(Inst, AbstractArena, finish)(inst); GlobalsFinish(ArenaGlobals(arena)); LocusFinish(arena); - RingFinish(&arena->chunkRing); + RingFinish(ArenaChunkRing(arena)); AVER(ArenaChunkTree(arena) == TreeEMPTY); } @@ -409,13 +447,11 @@ void ArenaFinish(Arena arena) /* ArenaDestroy -- destroy the arena */ static void arenaMFSPageFreeVisitor(Pool pool, Addr base, Size size, - void *closureP, Size closureS) + void *closure) { AVERT(Pool, pool); - AVER(closureP == UNUSED_POINTER); - AVER(closureS == UNUSED_SIZE); - UNUSED(closureP); - UNUSED(closureS); + AVER(closure == UNUSED_POINTER); + UNUSED(closure); UNUSED(size); AVER(size == ArenaGrainSize(PoolArena(pool))); arenaFreePage(PoolArena(pool), base, pool); @@ -433,8 +469,8 @@ static void arenaFreeLandFinish(Arena arena) /* The CBS block pool can't free its own memory via ArenaFree because * that would use the free land. */ - MFSFinishTracts(ArenaCBSBlockPool(arena), arenaMFSPageFreeVisitor, - UNUSED_POINTER, UNUSED_SIZE); + MFSFinishExtents(ArenaCBSBlockPool(arena), arenaMFSPageFreeVisitor, + UNUSED_POINTER); arena->hasFreeLand = FALSE; LandFinish(ArenaFreeLand(arena)); @@ -446,17 +482,14 @@ void ArenaDestroy(Arena arena) GlobalsPrepareToDestroy(ArenaGlobals(arena)); - /* Empty the reservoir - see */ - ReservoirSetLimit(ArenaReservoir(arena), 0); - 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); + /* Call class-specific destruction. This will call ArenaAbsFinish. */ + Method(Arena, arena, destroy)(arena); EventFinish(); } @@ -472,8 +505,8 @@ Res ControlInit(Arena arena) AVER(!arena->poolReady); MPS_ARGS_BEGIN(args) { MPS_ARGS_ADD(args, MPS_KEY_EXTEND_BY, CONTROL_EXTEND_BY); - res = PoolInit(MVPool(&arena->controlPoolStruct), arena, - PoolClassMV(), args); + res = PoolInit(ArenaControlPool(arena), arena, + PoolClassMVFF(), args); } MPS_ARGS_END(args); if (res != ResOK) return res; @@ -489,25 +522,23 @@ void ControlFinish(Arena arena) AVERT(Arena, arena); AVER(arena->poolReady); arena->poolReady = FALSE; - PoolFinish(MVPool(&arena->controlPoolStruct)); + PoolFinish(ArenaControlPool(arena)); } /* ArenaDescribe -- describe the arena */ -Res ArenaDescribe(Arena arena, mps_lib_FILE *stream, Count depth) +static Res ArenaAbsDescribe(Inst inst, mps_lib_FILE *stream, Count depth) { + Arena arena = CouldBeA(AbstractArena, inst); Res res; - if (!TESTT(Arena, arena)) - return ResFAIL; + if (!TESTC(AbstractArena, arena)) + return ResPARAM; if (stream == NULL) - return ResFAIL; + return ResPARAM; - res = WriteF(stream, depth, "Arena $P {\n", (WriteFP)arena, - " class $P (\"$S\")\n", - (WriteFP)arena->class, (WriteFS)arena->class->name, - NULL); + res = InstDescribe(CouldBeA(Inst, arena), stream, depth); if (res != ResOK) return res; @@ -544,27 +575,18 @@ Res ArenaDescribe(Arena arena, mps_lib_FILE *stream, Count depth) if (res != ResOK) return res; - res = (*arena->class->describe)(arena, stream, depth); + res = GlobalsDescribe(ArenaGlobals(arena), stream, depth + 2); if (res != ResOK) return res; - 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, depth, - "} Arena $P ($U)\n", (WriteFP)arena, - (WriteFU)arena->serial, - NULL); return res; } +Res ArenaDescribe(Arena arena, mps_lib_FILE *stream, Count depth) +{ + return Method(Inst, arena, describe)(MustBeA(Inst, arena), stream, depth); +} + /* arenaDescribeTractsInChunk -- describe the tracts in a chunk */ @@ -596,10 +618,11 @@ static Res arenaDescribeTractsInChunk(Chunk chunk, mps_lib_FILE *stream, Count d return res; if (TractHasPool(tract)) { Pool pool = TractPool(tract); + PoolClass poolClass = ClassOfPoly(Pool, pool); res = WriteF(stream, 0, " $P $U ($S)", (WriteFP)pool, (WriteFU)(pool->serial), - (WriteFS)(pool->class->name), + (WriteFS)ClassName(poolClass), NULL); if (res != ResOK) return res; @@ -629,7 +652,7 @@ Res ArenaDescribeTracts(Arena arena, mps_lib_FILE *stream, Count depth) if (stream == NULL) return ResFAIL; - RING_FOR(node, &arena->chunkRing, next) { + RING_FOR(node, ArenaChunkRing(arena), next) { Chunk chunk = RING_ELT(Chunk, arenaRing, node); res = arenaDescribeTractsInChunk(chunk, stream, depth); if (res != ResOK) @@ -649,8 +672,7 @@ Res ArenaDescribeTracts(Arena arena, mps_lib_FILE *stream, Count depth) * with void* (), ControlAlloc must take care of * allocating so that the block can be addressed with a void*. */ -Res ControlAlloc(void **baseReturn, Arena arena, size_t size, - Bool withReservoirPermit) +Res ControlAlloc(void **baseReturn, Arena arena, size_t size) { Addr base; Res res; @@ -658,11 +680,9 @@ Res ControlAlloc(void **baseReturn, Arena arena, size_t size, AVERT(Arena, arena); AVER(baseReturn != NULL); AVER(size > 0); - AVERT(Bool, withReservoirPermit); AVER(arena->poolReady); - res = PoolAlloc(&base, ArenaControlPool(arena), (Size)size, - withReservoirPermit); + res = PoolAlloc(&base, ArenaControlPool(arena), (Size)size); if (res != ResOK) return res; @@ -675,12 +695,15 @@ Res ControlAlloc(void **baseReturn, Arena arena, size_t size, void ControlFree(Arena arena, void* base, size_t size) { + Pool pool; + AVERT(Arena, arena); AVER(base != NULL); AVER(size > 0); AVER(arena->poolReady); - PoolFree(ArenaControlPool(arena), (Addr)base, (Size)size); + pool = ArenaControlPool(arena); + PoolFree(pool, (Addr)base, (Size)size); } @@ -706,7 +729,8 @@ Res ControlDescribe(Arena arena, mps_lib_FILE *stream, Count depth) * if not already set. */ -void ArenaChunkInsert(Arena arena, Chunk chunk) { +void ArenaChunkInsert(Arena arena, Chunk chunk) +{ Bool inserted; Tree tree, updatedTree = NULL; @@ -720,7 +744,7 @@ void ArenaChunkInsert(Arena arena, Chunk chunk) { AVER(updatedTree); TreeBalance(&updatedTree); arena->chunkTree = updatedTree; - RingAppend(&arena->chunkRing, &chunk->arenaRing); + RingAppend(ArenaChunkRing(arena), &chunk->arenaRing); arena->reserved += ChunkReserved(chunk); @@ -749,7 +773,7 @@ void ArenaChunkRemoved(Arena arena, Chunk chunk) if (chunk == arena->primary) { /* The primary chunk must be the last chunk to be removed. */ - AVER(RingIsSingle(&arena->chunkRing)); + AVER(RingIsSingle(ArenaChunkRing(arena))); AVER(arena->reserved == 0); arena->primary = NULL; } @@ -780,9 +804,9 @@ static Res arenaAllocPageInChunk(Addr *baseReturn, Chunk chunk, Pool pool) chunk->allocBase, chunk->pages, 1)) return ResRESOURCE; - res = (*arena->class->pagesMarkAllocated)(arena, chunk, - basePageIndex, 1, - pool); + res = Method(Arena, arena, pagesMarkAllocated)(arena, chunk, + basePageIndex, 1, + pool); if (res != ResOK) return res; @@ -804,7 +828,7 @@ static Res arenaAllocPage(Addr *baseReturn, Arena arena, Pool pool) res = arenaAllocPageInChunk(baseReturn, arena->primary, pool); if (res != ResOK) { Ring node, next; - RING_FOR(node, &arena->chunkRing, next) { + RING_FOR(node, ArenaChunkRing(arena), next) { Chunk chunk = RING_ELT(Chunk, arenaRing, node); if (chunk != arena->primary) { res = arenaAllocPageInChunk(baseReturn, chunk, pool); @@ -823,7 +847,7 @@ static void arenaFreePage(Arena arena, Addr base, Pool pool) { AVERT(Arena, arena); AVERT(Pool, pool); - (*arena->class->free)(base, ArenaGrainSize(arena), pool); + Method(Arena, arena, free)(base, ArenaGrainSize(arena), pool); } @@ -835,15 +859,16 @@ static void arenaFreePage(Arena arena, Addr base, Pool pool) static Res arenaExtendCBSBlockPool(Range pageRangeReturn, Arena arena) { - Addr pageBase; + Addr pageBase, pageLimit; Res res; res = arenaAllocPage(&pageBase, arena, ArenaCBSBlockPool(arena)); if (res != ResOK) return res; - MFSExtend(ArenaCBSBlockPool(arena), pageBase, ArenaGrainSize(arena)); + pageLimit = AddrAdd(pageBase, ArenaGrainSize(arena)); + MFSExtend(ArenaCBSBlockPool(arena), pageBase, pageLimit); - RangeInitSize(pageRangeReturn, pageBase, ArenaGrainSize(arena)); + RangeInit(pageRangeReturn, pageBase, pageLimit); return ResOK; } @@ -857,8 +882,9 @@ static void arenaExcludePage(Arena arena, Range pageRange) { RangeStruct oldRange; Res res; + Land land = ArenaFreeLand(arena); - res = LandDelete(&oldRange, ArenaFreeLand(arena), pageRange); + res = LandDelete(&oldRange, land, pageRange); AVER(res == ResOK); /* we just gave memory to the Land */ } @@ -878,12 +904,14 @@ static Res arenaFreeLandInsertExtend(Range rangeReturn, Arena arena, Range range) { Res res; + Land land; AVER(rangeReturn != NULL); AVERT(Arena, arena); AVERT(Range, range); - res = LandInsert(rangeReturn, ArenaFreeLand(arena), range); + land = ArenaFreeLand(arena); + res = LandInsert(rangeReturn, land, range); if (res == ResLIMIT) { /* CBS block pool ran out of blocks */ RangeStruct pageRange; @@ -892,7 +920,7 @@ static Res arenaFreeLandInsertExtend(Range rangeReturn, Arena arena, 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); + res = LandInsert(rangeReturn, land, range); AVER(res == ResOK); /* we just gave memory to the CBS block pool */ arenaExcludePage(arena, &pageRange); } @@ -923,25 +951,28 @@ static void arenaFreeLandInsertSteal(Range rangeReturn, Arena arena, res = arenaFreeLandInsertExtend(rangeReturn, arena, rangeIO); if (res != ResOK) { - Addr pageBase; + Land land; + Addr pageBase, pageLimit; 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)); + pageLimit = AddrAdd(pageBase, ArenaGrainSize(arena)); + AVER(pageLimit <= RangeLimit(rangeIO)); + RangeInit(rangeIO, pageLimit, 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)); + MFSExtend(ArenaCBSBlockPool(arena), pageBase, pageLimit); /* Try again. */ - res = LandInsert(rangeReturn, ArenaFreeLand(arena), rangeIO); + land = ArenaFreeLand(arena); + res = LandInsert(rangeReturn, land, rangeIO); AVER(res == ResOK); /* we just gave memory to the CBS block pool */ } @@ -963,6 +994,7 @@ Res ArenaFreeLandInsert(Arena arena, Addr base, Addr limit) Res res; AVERT(Arena, arena); + AVER(base < limit); RangeInit(&range, base, limit); res = arenaFreeLandInsertExtend(&oldRange, arena, &range); @@ -996,9 +1028,11 @@ void ArenaFreeLandDelete(Arena arena, Addr base, Addr limit) { RangeStruct range, oldRange; Res res; + Land land; RangeInit(&range, base, limit); - res = LandDelete(&oldRange, ArenaFreeLand(arena), &range); + land = ArenaFreeLand(arena); + res = LandDelete(&oldRange, land, &range); /* Shouldn't be any other kind of failure because we were only deleting a non-coalesced block. See .chunk.no-coalesce and @@ -1026,6 +1060,7 @@ Res ArenaFreeLandAlloc(Tract *tractReturn, Arena arena, ZoneSet zones, Index baseIndex; Count pages; Res res; + Land land; AVER(tractReturn != NULL); AVERT(Arena, arena); @@ -1040,8 +1075,8 @@ Res ArenaFreeLandAlloc(Tract *tractReturn, Arena arena, ZoneSet zones, /* Step 1. Find a range of address space. */ - res = LandFindInZones(&found, &range, &oldRange, ArenaFreeLand(arena), - size, zones, high); + land = ArenaFreeLand(arena); + res = LandFindInZones(&found, &range, &oldRange, land, size, zones, high); if (res == ResLIMIT) { /* found block, but couldn't store info */ RangeStruct pageRange; @@ -1049,8 +1084,7 @@ Res ArenaFreeLandAlloc(Tract *tractReturn, Arena arena, ZoneSet zones, if (res != ResOK) /* disastrously short on memory */ return res; arenaExcludePage(arena, &pageRange); - res = LandFindInZones(&found, &range, &oldRange, ArenaFreeLand(arena), - size, zones, high); + res = LandFindInZones(&found, &range, &oldRange, land, size, zones, high); AVER(res != ResLIMIT); } @@ -1069,7 +1103,7 @@ Res ArenaFreeLandAlloc(Tract *tractReturn, Arena arena, ZoneSet zones, baseIndex = INDEX_OF_ADDR(chunk, RangeBase(&range)); pages = ChunkSizeToPages(chunk, RangeSize(&range)); - res = (*arena->class->pagesMarkAllocated)(arena, chunk, baseIndex, pages, pool); + res = Method(Arena, arena, pagesMarkAllocated)(arena, chunk, baseIndex, pages, pool); if (res != ResOK) goto failMark; @@ -1093,45 +1127,25 @@ Res ArenaFreeLandAlloc(Tract *tractReturn, Arena arena, ZoneSet zones, /* ArenaAlloc -- allocate some tracts from the arena */ -Res ArenaAlloc(Addr *baseReturn, LocusPref pref, Size size, Pool pool, - Bool withReservoirPermit) +Res ArenaAlloc(Addr *baseReturn, LocusPref pref, Size size, Pool pool) { Res res; Arena arena; Addr base; Tract tract; - Reservoir reservoir; AVER(baseReturn != NULL); AVERT(LocusPref, pref); AVER(size > (Size)0); AVERT(Pool, pool); - AVERT(Bool, withReservoirPermit); arena = PoolArena(pool); AVERT(Arena, arena); AVER(SizeIsArenaGrains(size, arena)); - reservoir = ArenaReservoir(arena); - AVERT(Reservoir, reservoir); - - if (pool != ReservoirPool(reservoir)) { - res = ReservoirEnsureFull(reservoir); - if (res != ResOK) { - AVER(ResIsAllocFailure(res)); - if (!withReservoirPermit) - return res; - } - } 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; - } + if (res != ResOK) + goto allocFail; base = TractBase(tract); @@ -1156,8 +1170,6 @@ void ArenaFree(Addr base, Size size, Pool pool) { Arena arena; Addr limit; - Reservoir reservoir; - Res res; Addr wholeBase; Size wholeSize; RangeStruct range, oldRange; @@ -1167,8 +1179,6 @@ void ArenaFree(Addr base, Size size, Pool pool) AVER(size > (Size)0); arena = PoolArena(pool); AVERT(Arena, arena); - reservoir = ArenaReservoir(arena); - AVERT(Reservoir, reservoir); AVER(AddrIsArenaGrain(base, arena)); AVER(SizeIsArenaGrains(size, arena)); @@ -1182,30 +1192,16 @@ void ArenaFree(Addr base, Size size, Pool pool) 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); + Method(Arena, arena, free)(RangeBase(&range), RangeSize(&range), pool); /* Freeing memory might create spare pages, but not more than this. */ CHECKL((double)arena->spareCommitted / (arena->committed - arena->spareCommitted) <= arena->spare); -allDeposited: EVENT3(ArenaFree, arena, wholeBase, wholeSize); - return; } @@ -1246,10 +1242,24 @@ void ArenaSetSpare(Arena arena, double spare) spareMax = (Size)(arena->committed * arena->spare); if (arena->spareCommitted > spareMax) { Size excess = arena->spareCommitted - spareMax; - (void)arena->class->purgeSpare(arena, excess); + (void)Method(Arena, arena, purgeSpare)(arena, excess); } } +double ArenaPauseTime(Arena arena) +{ + AVERT(Arena, arena); + return arena->pauseTime; +} + +void ArenaSetPauseTime(Arena arena, double pauseTime) +{ + AVERT(Arena, arena); + AVER(0.0 <= pauseTime); + arena->pauseTime = pauseTime; + EVENT2(PauseTimeSet, arena, pauseTime); +} + /* Used by arenas which don't use spare committed memory */ Size ArenaNoPurgeSpare(Arena arena, Size size) { @@ -1287,7 +1297,7 @@ Res ArenaSetCommitLimit(Arena arena, Size limit) /* Attempt to set the limit below current committed */ if (limit >= committed - arena->spareCommitted) { Size excess = committed - limit; - (void)arena->class->purgeSpare(arena, excess); + (void)Method(Arena, arena, purgeSpare)(arena, excess); AVER(limit >= ArenaCommitted(arena)); arena->commitLimit = limit; res = ResOK; @@ -1320,6 +1330,7 @@ Size ArenaAvail(Arena arena) this information from the operating system. It also depends on the arena class, of course. */ + AVER(sSwap >= arena->committed); return sSwap - arena->committed + arena->spareCommitted; } @@ -1329,7 +1340,20 @@ Size ArenaAvail(Arena arena) Size ArenaCollectable(Arena arena) { /* Conservative estimate -- see job003929. */ - return ArenaCommitted(arena) - ArenaSpareCommitted(arena); + Size committed = ArenaCommitted(arena); + Size spareCommitted = ArenaSpareCommitted(arena); + AVER(committed >= spareCommitted); + return committed - spareCommitted; +} + + +/* ArenaAccumulateTime -- accumulate time spent tracing */ + +void ArenaAccumulateTime(Arena arena, Clock start, Clock end) +{ + AVERT(Arena, arena); + AVER(start <= end); + arena->tracedTime += (end - start) / (double) ClocksPerSec(); } @@ -1343,7 +1367,7 @@ Res ArenaExtend(Arena arena, Addr base, Size size) AVER(base != (Addr)0); AVER(size > 0); - res = (*arena->class->extend)(arena, base, size); + res = Method(Arena, arena, extend)(arena, base, size); if (res != ResOK) return res; @@ -1371,14 +1395,13 @@ void ArenaCompact(Arena arena, Trace trace) { AVERT(Arena, arena); AVERT(Trace, trace); - (*arena->class->compact)(arena, trace); + Method(Arena, arena, compact)(arena, trace); } static void ArenaTrivCompact(Arena arena, Trace trace) { UNUSED(arena); UNUSED(trace); - return; } @@ -1393,29 +1416,9 @@ Bool ArenaHasAddr(Arena arena, Addr addr) } -/* ArenaAddrObject -- find client pointer to object containing addr - * See job003589. - */ - -Res ArenaAddrObject(Addr *pReturn, Arena arena, Addr addr) -{ - Seg seg; - Pool pool; - - AVER(pReturn != NULL); - AVERT(Arena, arena); - - if (!SegOfAddr(&seg, arena, addr)) { - return ResFAIL; - } - pool = SegPool(seg); - return PoolAddrObject(pReturn, pool, seg, addr); -} - - /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/arenacl.c b/mps/code/arenacl.c index cd5c24fe46a..e2a0da37095 100644 --- a/mps/code/arenacl.c +++ b/mps/code/arenacl.c @@ -21,6 +21,8 @@ SRCID(arenacl, "$Id$"); +DECLARE_CLASS(Arena, ClientArena, AbstractArena); + /* ClientArenaStruct -- Client Arena Structure */ @@ -32,9 +34,6 @@ typedef struct ClientArenaStruct { } ClientArenaStruct; typedef struct ClientArenaStruct *ClientArena; -#define Arena2ClientArena(arena) PARENT(ClientArenaStruct, arenaStruct, arena) -#define ClientArena2Arena(clArena) (&(clArena)->arenaStruct) - /* CLChunk -- chunk structure */ @@ -81,11 +80,8 @@ static Bool ClientChunkCheck(ClientChunk clChunk) ATTRIBUTE_UNUSED static Bool ClientArenaCheck(ClientArena clientArena) { - Arena arena; + Arena arena = MustBeA(AbstractArena, clientArena); - CHECKS(ClientArena, clientArena); - arena = ClientArena2Arena(clientArena); - CHECKD(Arena, arena); /* See */ CHECKL(arena->committed <= arena->reserved); CHECKL(arena->spareCommitted == 0); @@ -99,7 +95,7 @@ static Bool ClientArenaCheck(ClientArena clientArena) static Res clientChunkCreate(Chunk *chunkReturn, ClientArena clientArena, Addr base, Addr limit) { - Arena arena; + Arena arena = MustBeA(AbstractArena, clientArena); ClientChunk clChunk; Chunk chunk; Addr alignedBase; @@ -109,8 +105,6 @@ static Res clientChunkCreate(Chunk *chunkReturn, ClientArena clientArena, void *p; AVER(chunkReturn != NULL); - AVERT(ClientArena, clientArena); - arena = ClientArena2Arena(clientArena); AVER(base != (Addr)0); AVER(limit != (Addr)0); AVER(limit > base); @@ -182,7 +176,7 @@ static Res ClientChunkInit(Chunk chunk, BootBlock boot) /* clientChunkDestroy -- destroy a ClientChunk */ -static Bool clientChunkDestroy(Tree tree, void *closureP, Size closureS) +static Bool clientChunkDestroy(Tree tree, void *closure) { Arena arena; Chunk chunk; @@ -190,10 +184,8 @@ static Bool clientChunkDestroy(Tree tree, void *closureP, Size closureS) Size size; AVERT(Tree, tree); - AVER(closureP == UNUSED_POINTER); - UNUSED(closureP); - AVER(closureS == UNUSED_SIZE); - UNUSED(closureS); + AVER(closure == UNUSED_POINTER); + UNUSED(closure); chunk = ChunkOfTree(tree); AVERT(Chunk, chunk); @@ -246,7 +238,7 @@ static void ClientArenaVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) ARG_DEFINE_KEY(ARENA_CL_BASE, Addr); -static Res ClientArenaInit(Arena *arenaReturn, ArenaClass class, ArgList args) +static Res ClientArenaCreate(Arena *arenaReturn, ArgList args) { Arena arena; ClientArena clientArena; @@ -259,7 +251,6 @@ static Res ClientArenaInit(Arena *arenaReturn, ArenaClass class, ArgList args) mps_arg_s arg; AVER(arenaReturn != NULL); - AVER((ArenaClass)mps_arena_class_cl() == class); AVERT(ArgList, args); ArgRequire(&arg, args, MPS_KEY_ARENA_SIZE); @@ -291,11 +282,13 @@ static Res ClientArenaInit(Arena *arenaReturn, ArenaClass class, ArgList args) if (chunkBase > limit) return ResMEMORY; - arena = ClientArena2Arena(clientArena); - /* */ - res = ArenaInit(arena, class, grainSize, args); + arena = CouldBeA(AbstractArena, clientArena); + + res = NextMethod(Arena, ClientArena, init)(arena, grainSize, args); if (res != ResOK) - return res; + goto failSuperInit; + SetClassOfPoly(arena, CLASS(ClientArena)); + AVER(clientArena == MustBeA(ClientArena, arena)); /* have to have a valid arena before calling ChunkCreate */ clientArena->sig = ClientArenaSig; @@ -318,26 +311,24 @@ static Res ClientArenaInit(Arena *arenaReturn, ArenaClass class, ArgList args) return ResOK; failChunkCreate: - ArenaFinish(arena); + NextMethod(Inst, ClientArena, finish)(MustBeA(Inst, arena)); +failSuperInit: AVER(res != ResOK); return res; } -/* ClientArenaFinish -- finish the arena */ +/* ClientArenaDestroy -- destroy the arena */ -static void ClientArenaFinish(Arena arena) +static void ClientArenaDestroy(Arena arena) { - ClientArena clientArena; - - clientArena = Arena2ClientArena(arena); - AVERT(ClientArena, clientArena); + ClientArena clientArena = MustBeA(ClientArena, arena); /* Destroy all chunks, including the primary. See * */ arena->primary = NULL; TreeTraverseAndDelete(&arena->chunkTree, clientChunkDestroy, - UNUSED_POINTER, UNUSED_SIZE); + UNUSED_POINTER); clientArena->sig = SigInvalid; @@ -345,7 +336,7 @@ static void ClientArenaFinish(Arena arena) AVER(arena->reserved == 0); AVER(arena->committed == 0); - ArenaFinish(arena); /* */ + NextMethod(Inst, ClientArena, finish)(MustBeA(Inst, arena)); } @@ -353,19 +344,13 @@ static void ClientArenaFinish(Arena arena) static Res ClientArenaExtend(Arena arena, Addr base, Size size) { - ClientArena clientArena; + ClientArena clientArena = MustBeA(ClientArena, arena); Chunk chunk; - Res res; - Addr limit; - AVERT(Arena, arena); AVER(base != (Addr)0); AVER(size > 0); - limit = AddrAdd(base, size); - clientArena = Arena2ClientArena(arena); - res = clientChunkCreate(&chunk, clientArena, base, limit); - return res; + return clientChunkCreate(&chunk, clientArena, base, AddrAdd(base, size)); } @@ -398,6 +383,20 @@ static Res ClientArenaPagesMarkAllocated(Arena arena, Chunk chunk, } +/* ClientChunkPageMapped -- determine if a page is mapped */ + +static Bool ClientChunkPageMapped(Chunk chunk, Index index) +{ + UNUSED(chunk); + UNUSED(index); + + AVERT(Chunk, chunk); + AVER(index < chunk->pages); + + return TRUE; +} + + /* ClientArenaFree - free a region in the arena */ static void ClientArenaFree(Addr base, Size size, Pool pool) @@ -405,7 +404,6 @@ static void ClientArenaFree(Addr base, Size size, Pool pool) Arena arena; Chunk chunk = NULL; /* suppress "may be used uninitialized" */ Size pages; - ClientArena clientArena; Index pi, baseIndex, limitIndex; Bool foundChunk; ClientChunk clChunk; @@ -414,9 +412,7 @@ static void ClientArenaFree(Addr base, Size size, Pool pool) AVER(size > (Size)0); AVERT(Pool, pool); arena = PoolArena(pool); - AVERT(Arena, arena); - clientArena = Arena2ClientArena(arena); - AVERT(ClientArena, clientArena); + AVERC(ClientArena, arena); AVER(SizeIsAligned(size, ChunkPageSize(arena->primary))); AVER(AddrIsAligned(base, ChunkPageSize(arena->primary))); @@ -449,21 +445,20 @@ static void ClientArenaFree(Addr base, Size size, Pool pool) /* ClientArenaClass -- The Client arena class definition */ -DEFINE_ARENA_CLASS(ClientArenaClass, this) +DEFINE_CLASS(Arena, ClientArena, klass) { - INHERIT_CLASS(this, AbstractArenaClass); - this->name = "CL"; - this->size = sizeof(ClientArenaStruct); - this->offset = offsetof(ClientArenaStruct, arenaStruct); - this->varargs = ClientArenaVarargs; - this->init = ClientArenaInit; - this->finish = ClientArenaFinish; - this->extend = ClientArenaExtend; - this->pagesMarkAllocated = ClientArenaPagesMarkAllocated; - this->free = ClientArenaFree; - this->chunkInit = ClientChunkInit; - this->chunkFinish = ClientChunkFinish; - AVERT(ArenaClass, this); + INHERIT_CLASS(klass, ClientArena, AbstractArena); + klass->size = sizeof(ClientArenaStruct); + klass->varargs = ClientArenaVarargs; + klass->create = ClientArenaCreate; + klass->destroy = ClientArenaDestroy; + klass->extend = ClientArenaExtend; + klass->pagesMarkAllocated = ClientArenaPagesMarkAllocated; + klass->free = ClientArenaFree; + klass->chunkInit = ClientChunkInit; + klass->chunkFinish = ClientChunkFinish; + klass->chunkPageMapped = ClientChunkPageMapped; + AVERT(ArenaClass, klass); } @@ -471,7 +466,7 @@ DEFINE_ARENA_CLASS(ClientArenaClass, this) mps_arena_class_t mps_arena_class_cl(void) { - return (mps_arena_class_t)EnsureClientArenaClass(); + return (mps_arena_class_t)CLASS(ClientArena); } diff --git a/mps/code/arenacv.c b/mps/code/arenacv.c index 7b02c11bcc2..4aa57909d1b 100644 --- a/mps/code/arenacv.c +++ b/mps/code/arenacv.c @@ -15,7 +15,7 @@ */ #include "mpm.h" -#include "poolmv.h" +#include "poolmvff.h" #include "testlib.h" #include "mpslib.h" #include "mpsavm.h" @@ -161,7 +161,7 @@ static Res allocAsTract(AllocInfoStruct *aiReturn, LocusPref pref, { Res res; Addr base; - res = ArenaAlloc(&base, pref, size, pool, FALSE); + res = ArenaAlloc(&base, pref, size, pool); if (res == ResOK) { aiReturn->the.tractData.base = base; aiReturn->the.tractData.size = size; @@ -249,7 +249,7 @@ static Res allocAsSeg(AllocInfoStruct *aiReturn, LocusPref pref, { Res res; Seg seg; - res = SegAlloc(&seg, SegClassGet(), pref, size, pool, FALSE, argsNone); + res = SegAlloc(&seg, CLASS(Seg), pref, size, pool, argsNone); if (res == ResOK) { aiReturn->the.segData.seg = seg; } @@ -402,7 +402,7 @@ static void testAllocAndIterate(Arena arena, Pool pool, } -static void testPageTable(ArenaClass class, Size size, Addr addr, Bool zoned) +static void testPageTable(ArenaClass klass, Size size, Addr addr, Bool zoned) { Arena arena; Pool pool; Size pageSize; @@ -412,10 +412,10 @@ static void testPageTable(ArenaClass class, Size size, Addr addr, Bool zoned) 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"); + die(ArenaCreate(&arena, klass, args), "ArenaCreate"); } MPS_ARGS_END(args); - die(PoolCreate(&pool, arena, PoolClassMV(), argsNone), "PoolCreate"); + die(PoolCreate(&pool, arena, PoolClassMVFF(), argsNone), "PoolCreate"); pageSize = ArenaGrainSize(arena); tractsPerPage = pageSize / sizeof(TractStruct); @@ -446,14 +446,14 @@ static void testPageTable(ArenaClass class, Size size, Addr addr, Bool zoned) static void testSize(Size size) { - ArenaClass class = (ArenaClass)mps_arena_class_vm(); + ArenaClass klass = (ArenaClass)mps_arena_class_vm(); Arena arena; Res res; do { MPS_ARGS_BEGIN(args) { MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, size); - res = ArenaCreate(&arena, class, args); + res = ArenaCreate(&arena, klass, args); } MPS_ARGS_END(args); if (res == ResOK) ArenaDestroy(arena); diff --git a/mps/code/arenavm.c b/mps/code/arenavm.c index ac831b6fbd1..6f4839be904 100644 --- a/mps/code/arenavm.c +++ b/mps/code/arenavm.c @@ -1,7 +1,7 @@ /* arenavm.c: VIRTUAL MEMORY ARENA CLASS * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. * * * DESIGN @@ -56,7 +56,7 @@ typedef struct VMChunkStruct { /* VMChunkVMArena -- get the VM arena from a VM chunk */ #define VMChunkVMArena(vmchunk) \ - Arena2VMArena(ChunkArena(VMChunk2Chunk(vmchunk))) + MustBeA(VMArena, ChunkArena(VMChunk2Chunk(vmchunk))) /* VMArena @@ -81,8 +81,6 @@ typedef struct VMArenaStruct { /* VM arena structure */ Sig sig; /* */ } VMArenaStruct; -#define Arena2VMArena(arena) PARENT(VMArenaStruct, arenaStruct, arena) -#define VMArena2Arena(vmarena) (&(vmarena)->arenaStruct) #define VMArenaVM(vmarena) (&(vmarena)->vmStruct) @@ -90,7 +88,7 @@ typedef struct VMArenaStruct { /* VM arena structure */ static Size VMPurgeSpare(Arena arena, Size size); static void chunkUnmapSpare(Chunk chunk); -extern ArenaClass VMArenaClassGet(void); +DECLARE_CLASS(Arena, VMArena, AbstractArena); static void VMCompact(Arena arena, Trace trace); @@ -163,7 +161,7 @@ static Bool VMArenaCheck(VMArena vmArena) VMChunk primary; CHECKS(VMArena, vmArena); - arena = VMArena2Arena(vmArena); + arena = MustBeA(AbstractArena, vmArena); CHECKD(Arena, arena); /* spare pages are committed, so must be less spare than committed. */ CHECKL(vmArena->spareSize <= arena->committed); @@ -189,29 +187,20 @@ static Bool VMArenaCheck(VMArena vmArena) /* VMArenaDescribe -- describe the VMArena */ -static Res VMArenaDescribe(Arena arena, mps_lib_FILE *stream, Count depth) +static Res VMArenaDescribe(Inst inst, mps_lib_FILE *stream, Count depth) { + Arena arena = CouldBeA(AbstractArena, inst); + VMArena vmArena = CouldBeA(VMArena, arena); Res res; - VMArena vmArena; - if (!TESTT(Arena, arena)) - return ResFAIL; + if (!TESTC(VMArena, vmArena)) + return ResPARAM; if (stream == NULL) - return ResFAIL; - vmArena = Arena2VMArena(arena); - if (!TESTT(VMArena, vmArena)) - return ResFAIL; + return ResPARAM; - /* Describe the superclass fields first via next-method call */ - /* ...but the next method is ArenaTrivDescribe, so don't call it; - * see impl.c.arena#describe.triv.dont-upcall. - * - super = ARENA_SUPERCLASS(VMArenaClass); - res = super->describe(arena, stream); + res = NextMethod(Inst, VMArena, describe)(inst, stream, depth); if (res != ResOK) return res; - * - */ res = WriteF(stream, depth, " spareSize: $U\n", (WriteFU)vmArena->spareSize, @@ -219,7 +208,7 @@ static Res VMArenaDescribe(Arena arena, mps_lib_FILE *stream, Count depth) if(res != ResOK) return res; - /* (incomplete: some fields are not Described) */ + /* TODO: incomplete -- some fields are not Described */ return ResOK; } @@ -234,14 +223,12 @@ static Res VMArenaDescribe(Arena arena, mps_lib_FILE *stream, Count depth) */ static Res vmArenaMap(VMArena vmArena, VM vm, Addr base, Addr limit) { - Arena arena; - Size size; + Arena arena = MustBeA(AbstractArena, vmArena); + Size size = AddrOffset(base, limit); Res res; /* no checking as function is local to module */ - arena = VMArena2Arena(vmArena); - size = AddrOffset(base, limit); /* committed can't overflow (since we can't commit more memory than */ /* address space), but we're paranoid. */ AVER(arena->committed < arena->committed + size); @@ -259,18 +246,14 @@ static Res vmArenaMap(VMArena vmArena, VM vm, Addr base, Addr limit) static void vmArenaUnmap(VMArena vmArena, VM vm, Addr base, Addr limit) { - Arena arena; - Size size; + Arena arena = MustBeA(AbstractArena, vmArena); + Size size = AddrOffset(base, limit); /* no checking as function is local to module */ - - arena = VMArena2Arena(vmArena); - size = AddrOffset(base, limit); AVER(size <= arena->committed); VMUnmap(vm, base, limit); arena->committed -= size; - return; } @@ -282,7 +265,7 @@ static void vmArenaUnmap(VMArena vmArena, VM vm, Addr base, Addr limit) */ static Res VMChunkCreate(Chunk *chunkReturn, VMArena vmArena, Size size) { - Arena arena; + Arena arena = MustBeA(AbstractArena, vmArena); Res res; Addr base, limit, chunkStructLimit; VMStruct vmStruct; @@ -294,7 +277,6 @@ static Res VMChunkCreate(Chunk *chunkReturn, VMArena vmArena, Size size) AVER(chunkReturn != NULL); AVERT(VMArena, vmArena); - arena = VMArena2Arena(vmArena); AVER(size > 0); res = VMInit(vm, size, ArenaGrainSize(arena), vmArena->vmParams); @@ -308,8 +290,7 @@ static Res VMChunkCreate(Chunk *chunkReturn, VMArena vmArena, Size size) if (res != ResOK) goto failBootInit; - /* Allocate and map the descriptor. */ - /* See .@@@@ */ + /* .overhead.chunk-struct: Allocate and map the chunk structure. */ res = BootAlloc(&p, boot, sizeof(VMChunkStruct), MPS_PF_ALIGN); if (res != ResOK) goto failChunkAlloc; @@ -361,11 +342,13 @@ static Res VMChunkInit(Chunk chunk, BootBlock boot) vmChunk = Chunk2VMChunk(chunk); AVERT(BootBlock, boot); + /* .overhead.sa-mapped: Chunk overhead for sparse array 'mapped' table. */ res = BootAlloc(&p, boot, BTSize(chunk->pages), MPS_PF_ALIGN); if (res != ResOK) goto failSaMapped; saMapped = p; + /* .overhead.sa-pages: Chunk overhead for sparse array 'pages' table. */ res = BootAlloc(&p, boot, BTSize(chunk->pageTablePages), MPS_PF_ALIGN); if (res != ResOK) goto failSaPages; @@ -373,8 +356,8 @@ static Res VMChunkInit(Chunk chunk, BootBlock boot) overheadLimit = AddrAdd(chunk->base, (Size)BootAllocated(boot)); - /* Put the page table as late as possible, as in VM systems we don't want */ - /* to map it. */ + /* .overhead.page-table: Put the page table as late as possible, as + * in VM systems we don't want to map it. */ res = BootAlloc(&p, boot, chunk->pageTablePages << chunk->pageShift, chunk->pageSize); if (res != ResOK) goto failAllocPageTable; @@ -409,16 +392,14 @@ static Res VMChunkInit(Chunk chunk, BootBlock boot) /* vmChunkDestroy -- destroy a VMChunk */ -static Bool vmChunkDestroy(Tree tree, void *closureP, Size closureS) +static Bool vmChunkDestroy(Tree tree, void *closure) { Chunk chunk; VMChunk vmChunk; AVERT(Tree, tree); - AVER(closureP == UNUSED_POINTER); - UNUSED(closureP); - AVER(closureS == UNUSED_SIZE); - UNUSED(closureS); + AVER(closure == UNUSED_POINTER); + UNUSED(closure); chunk = ChunkOfTree(tree); AVERT(Chunk, chunk); @@ -493,7 +474,66 @@ static void vmArenaTrivContracted(Arena arena, Addr base, Size size) } -/* VMArenaInit -- create and initialize the VM arena +/* vmArenaChunkSize -- compute chunk size + * + * Compute the size of the smallest chunk that has size bytes of usable + * address space (that is, after all overheads are accounted for). + * + * If successful, update *chunkSizeReturn with the computed chunk size + * and return ResOK. If size is too large for a chunk, leave + * *chunkSizeReturn unchanged and return ResRESOURCE. + */ +static Res vmArenaChunkSize(Size *chunkSizeReturn, VMArena vmArena, Size size) +{ + Size grainSize; /* Arena grain size. */ + Shift grainShift; /* The corresponding Shift. */ + Count pages; /* Number of usable pages in chunk. */ + Size pageTableSize; /* Size of the page table. */ + Count pageTablePages; /* Number of pages in the page table. */ + Size chunkSize; /* Size of the chunk. */ + Size overhead; /* Total overheads for the chunk. */ + + AVER(chunkSizeReturn != NULL); + AVERT(VMArena, vmArena); + AVER(size > 0); + + grainSize = ArenaGrainSize(MustBeA(AbstractArena, vmArena)); + grainShift = SizeLog2(grainSize); + + overhead = 0; + do { + chunkSize = size + overhead; + AVER(SizeIsAligned(chunkSize, grainSize)); + + /* See .overhead.chunk-struct. */ + overhead = SizeAlignUp(sizeof(VMChunkStruct), MPS_PF_ALIGN); + + /* See , */ + pages = chunkSize >> grainShift; + overhead += SizeAlignUp(BTSize(pages), MPS_PF_ALIGN); + + /* See .overhead.sa-mapped. */ + overhead += SizeAlignUp(BTSize(pages), MPS_PF_ALIGN); + + /* See .overhead.sa-pages. */ + pageTableSize = SizeAlignUp(pages * sizeof(PageUnion), grainSize); + pageTablePages = pageTableSize >> grainShift; + overhead += SizeAlignUp(BTSize(pageTablePages), MPS_PF_ALIGN); + + /* See .overhead.page-table. */ + overhead = SizeAlignUp(overhead, grainSize); + overhead += SizeAlignUp(pageTableSize, grainSize); + + if (SizeMAX - overhead < size) + return ResRESOURCE; + } while (chunkSize < size + overhead); + + *chunkSizeReturn = chunkSize; + return ResOK; +} + + +/* VMArenaCreate -- create and initialize the VM arena * * .arena.init: Once the arena has been allocated, we call ArenaInit * to do the generic part of init. @@ -504,7 +544,7 @@ ARG_DEFINE_KEY(arena_extended, Fun); ARG_DEFINE_KEY(arena_contracted, Fun); #define vmKeyArenaContracted (&_mps_key_arena_contracted) -static Res VMArenaInit(Arena *arenaReturn, ArenaClass class, ArgList args) +static Res VMArenaCreate(Arena *arenaReturn, ArgList args) { Size size = VM_ARENA_SIZE_DEFAULT; /* initial arena size */ Align grainSize = MPS_PF_ALIGN; /* arena grain size */ @@ -521,7 +561,6 @@ static Res VMArenaInit(Arena *arenaReturn, ArenaClass class, ArgList args) char vmParams[VMParamSize]; AVER(arenaReturn != NULL); - AVER(class == VMArenaClassGet()); AVERT(ArgList, args); if (ArgPick(&arg, args, MPS_KEY_ARENA_GRAIN_SIZE)) @@ -556,11 +595,14 @@ static Res VMArenaInit(Arena *arenaReturn, ArenaClass class, ArgList args) goto failVMMap; vmArena = (VMArena)VMBase(vm); - arena = VMArena2Arena(vmArena); - /* */ - res = ArenaInit(arena, class, grainSize, args); + arena = CouldBeA(AbstractArena, vmArena); + + res = NextMethod(Arena, VMArena, init)(arena, grainSize, args); if (res != ResOK) goto failArenaInit; + SetClassOfPoly(arena, CLASS(VMArena)); + AVER(vmArena == MustBeA(VMArena, arena)); + arena->reserved = VMReserved(vm); arena->committed = VMMapped(vm); @@ -591,6 +633,22 @@ static Res VMArenaInit(Arena *arenaReturn, ArenaClass class, ArgList args) if (res != ResOK) goto failChunkCreate; +#if defined(AVER_AND_CHECK_ALL) + /* Check the computation of the chunk size in vmArenaChunkSize, now + * that we have the actual chunk for comparison. Note that + * vmArenaChunkSize computes the smallest size with a given number + * of usable bytes -- the actual chunk may be one grain larger. */ + { + Size usableSize, computedChunkSize; + usableSize = AddrOffset(PageIndexBase(chunk, chunk->allocBase), + chunk->limit); + res = vmArenaChunkSize(&computedChunkSize, vmArena, usableSize); + AVER(res == ResOK); + AVER(computedChunkSize == ChunkSize(chunk) + || computedChunkSize + grainSize == ChunkSize(chunk)); + } +#endif + /* .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 */ @@ -609,7 +667,7 @@ static Res VMArenaInit(Arena *arenaReturn, ArenaClass class, ArgList args) return ResOK; failChunkCreate: - ArenaFinish(arena); + NextMethod(Inst, VMArena, finish)(MustBeA(Inst, arena)); failArenaInit: VMUnmap(vm, VMBase(vm), VMLimit(vm)); failVMMap: @@ -619,16 +677,13 @@ static Res VMArenaInit(Arena *arenaReturn, ArenaClass class, ArgList args) } -/* VMArenaFinish -- finish the arena */ +/* VMArenaDestroy -- destroy the arena */ -static void VMArenaFinish(Arena arena) +static void VMArenaDestroy(Arena arena) { + VMArena vmArena = MustBeA(VMArena, arena); VMStruct vmStruct; VM vm = &vmStruct; - VMArena vmArena; - - vmArena = Arena2VMArena(arena); - AVERT(VMArena, vmArena); EVENT1(ArenaDestroy, vmArena); @@ -636,7 +691,7 @@ static void VMArenaFinish(Arena arena) * */ arena->primary = NULL; TreeTraverseAndDelete(&arena->chunkTree, vmChunkDestroy, - UNUSED_POINTER, UNUSED_SIZE); + UNUSED_POINTER); /* Destroying the chunks should have purged and removed all spare pages. */ RingFinish(&vmArena->spareRing); @@ -647,7 +702,7 @@ static void VMArenaFinish(Arena arena) vmArena->sig = SigInvalid; - ArenaFinish(arena); /* */ + NextMethod(Inst, VMArena, finish)(MustBeA(Inst, arena)); /* Copy VM descriptor to stack-local storage so that we can continue * using the descriptor after the VM has been unmapped. */ @@ -657,63 +712,34 @@ static void VMArenaFinish(Arena arena) } -/* vmArenaChunkSize -- choose chunk size for arena extension - * - * .vmchunk.overhead: This code still lacks a proper estimate of - * the overhead required by a vmChunk for chunkStruct, page tables - * etc. For now, estimate it as 10%. RHSK 2007-12-21 - */ -static Size vmArenaChunkSize(VMArena vmArena, Size size) -{ - Size fraction = 10; /* 10% -- see .vmchunk.overhead */ - Size chunkSize; - Size chunkOverhead; - - /* 1: use extendBy, if it is big enough for size + overhead */ - chunkSize = vmArena->extendBy; - chunkOverhead = chunkSize / fraction; - if(chunkSize > size && (chunkSize - size) >= chunkOverhead) - return chunkSize; - - /* 2: use size + overhead (unless it overflows SizeMAX) */ - chunkOverhead = size / (fraction - 1); - if((SizeMAX - size) >= chunkOverhead) - return size + chunkOverhead; - - /* 3: use SizeMAX */ - return SizeMAX; -} - - /* VMArenaGrow -- Extend the arena by making a new chunk * - * The size arg specifies how much we wish to allocate after the extension. + * size specifies how much we wish to allocate after the extension. + * pref specifies the preference for the location of the allocation. */ static Res VMArenaGrow(Arena arena, LocusPref pref, Size size) { + VMArena vmArena = MustBeA(VMArena, arena); Chunk newChunk; Size chunkSize; + Size chunkMin; 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); + res = vmArenaChunkSize(&chunkMin, vmArena, size); + if (res != ResOK) + return res; + chunkSize = vmArena->extendBy; - EVENT3(vmArenaExtendStart, size, chunkSize, - ArenaReserved(VMArena2Arena(vmArena))); + EVENT3(vmArenaExtendStart, size, chunkSize, ArenaReserved(arena)); /* .chunk-create.fail: If we fail, try again with a smaller size */ { unsigned fidelity = 8; /* max fraction of addr-space we may 'waste' */ Size chunkHalf; - Size chunkMin = 4 * 1024; /* typical single page */ Size sliceSize; if (vmArena->extendMin > chunkMin) @@ -730,8 +756,7 @@ static Res VMArenaGrow(Arena arena, LocusPref pref, Size size) /* remove slices, down to chunkHalf but no further */ for(; chunkSize > chunkHalf; chunkSize -= sliceSize) { if(chunkSize < chunkMin) { - EVENT2(vmArenaExtendFail, chunkMin, - ArenaReserved(VMArena2Arena(vmArena))); + EVENT2(vmArenaExtendFail, chunkMin, ArenaReserved(arena)); return res; } res = VMChunkCreate(&newChunk, vmArena, chunkSize); @@ -742,8 +767,8 @@ static Res VMArenaGrow(Arena arena, LocusPref pref, Size size) } vmArenaGrow_Done: - EVENT2(vmArenaExtendDone, chunkSize, ArenaReserved(VMArena2Arena(vmArena))); - vmArena->extended(VMArena2Arena(vmArena), + EVENT2(vmArenaExtendDone, chunkSize, ArenaReserved(arena)); + vmArena->extended(arena, newChunk->base, AddrOffset(newChunk->base, newChunk->limit)); @@ -788,7 +813,7 @@ static void sparePageRelease(VMChunk vmChunk, Index pi) static Res pageDescMap(VMChunk vmChunk, Index basePI, Index limitPI) { Size before = VMMapped(VMChunkVM(vmChunk)); - Arena arena = VMArena2Arena(VMChunkVMArena(vmChunk)); + Arena arena = MustBeA(AbstractArena, VMChunkVMArena(vmChunk)); Res res = SparseArrayMap(&vmChunk->pages, basePI, limitPI); Size after = VMMapped(VMChunkVM(vmChunk)); AVER(before <= after); @@ -800,7 +825,7 @@ static void pageDescUnmap(VMChunk vmChunk, Index basePI, Index limitPI) { Size size, after; Size before = VMMapped(VMChunkVM(vmChunk)); - Arena arena = VMArena2Arena(VMChunkVMArena(vmChunk)); + Arena arena = MustBeA(AbstractArena, VMChunkVMArena(vmChunk)); SparseArrayUnmap(&vmChunk->pages, basePI, limitPI); after = VMMapped(VMChunkVM(vmChunk)); AVER(after <= before); @@ -874,6 +899,7 @@ static Res VMPagesMarkAllocated(Arena arena, Chunk chunk, Index baseIndex, Count pages, Pool pool) { Res res; + VMArena vmArena = MustBeA(VMArena, arena); AVERT(Arena, arena); AVERT(Chunk, chunk); @@ -882,7 +908,7 @@ static Res VMPagesMarkAllocated(Arena arena, Chunk chunk, AVER(baseIndex + pages <= chunk->pages); AVERT(Pool, pool); - res = pagesMarkAllocated(Arena2VMArena(arena), + res = pagesMarkAllocated(vmArena, Chunk2VMChunk(chunk), baseIndex, pages, @@ -896,7 +922,7 @@ static Res VMPagesMarkAllocated(Arena arena, Chunk chunk, success if we have enough spare pages. */ if (VMPurgeSpare(arena, pages * ChunkPageSize(chunk)) == 0) break; - res = pagesMarkAllocated(Arena2VMArena(arena), + res = pagesMarkAllocated(vmArena, Chunk2VMChunk(chunk), baseIndex, pages, @@ -906,6 +932,16 @@ static Res VMPagesMarkAllocated(Arena arena, Chunk chunk, } +static Bool VMChunkPageMapped(Chunk chunk, Index index) +{ + VMChunk vmChunk; + AVERT(Chunk, chunk); + AVER(index < chunk->pages); + vmChunk = Chunk2VMChunk(chunk); + return BTGet(vmChunk->pages.mapped, index); +} + + /* chunkUnmapAroundPage -- unmap spare pages in a chunk including this one * * Unmap the spare page passed, and possibly other pages in the chunk, @@ -972,13 +1008,10 @@ static Size chunkUnmapAroundPage(Chunk chunk, Size size, Page page) static Size arenaUnmapSpare(Arena arena, Size size, Chunk filter) { + VMArena vmArena = MustBeA(VMArena, arena); Ring node; Size purged = 0; - VMArena vmArena; - AVERT(Arena, arena); - vmArena = Arena2VMArena(arena); - AVERT(VMArena, vmArena); if (filter != NULL) AVERT(Chunk, filter); @@ -1040,9 +1073,7 @@ static void VMFree(Addr base, Size size, Pool pool) AVER(size > (Size)0); AVERT(Pool, pool); arena = PoolArena(pool); - AVERT(Arena, arena); - vmArena = Arena2VMArena(arena); - AVERT(VMArena, vmArena); + vmArena = MustBeA(VMArena, arena); /* All chunks have same pageSize. */ AVER(SizeIsAligned(size, ChunkPageSize(arena->primary))); @@ -1106,19 +1137,14 @@ static void VMFree(Addr base, Size size, Pool pool) /* vmChunkCompact -- delete chunk if empty and not primary */ -static Bool vmChunkCompact(Tree tree, void *closureP, Size closureS) +static Bool vmChunkCompact(Tree tree, void *closure) { Chunk chunk; - Arena arena = closureP; - VMArena vmArena; + Arena arena = closure; + VMArena vmArena = MustBeA(VMArena, arena); AVERT(Tree, tree); - AVERT(Arena, arena); - AVER(closureS == UNUSED_SIZE); - UNUSED(closureS); - vmArena = Arena2VMArena(arena); - AVERT(VMArena, vmArena); chunk = ChunkOfTree(tree); AVERT(Chunk, chunk); if(chunk != arena->primary @@ -1129,7 +1155,7 @@ static Bool vmChunkCompact(Tree tree, void *closureP, Size closureS) /* 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); + vmChunkDestroy(tree, UNUSED_POINTER); return TRUE; } else { /* Keep this chunk. */ @@ -1140,34 +1166,26 @@ static Bool vmChunkCompact(Tree tree, void *closureP, Size closureS) static void VMCompact(Arena arena, Trace trace) { - VMArena vmArena; - Size vmem1; + STATISTIC_DECL(Size vmem1) - vmArena = Arena2VMArena(arena); - AVERT(VMArena, vmArena); AVERT(Trace, trace); - vmem1 = ArenaReserved(arena); + STATISTIC(vmem1 = ArenaReserved(arena)); /* 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); + TreeTraverseAndDelete(&arena->chunkTree, vmChunkCompact, arena); - { + STATISTIC({ Size vmem0 = trace->preTraceArenaReserved; Size vmem2 = ArenaReserved(arena); - /* VMCompact event: emit for all client-requested collections, */ - /* plus any others where chunks were gained or lost during the */ - /* collection. */ - if(trace->why == TraceStartWhyCLIENTFULL_INCREMENTAL - || trace->why == TraceStartWhyCLIENTFULL_BLOCK - || vmem0 != vmem1 - || vmem1 != vmem2) + /* VMCompact event: emit for collections where chunks were gained + * or lost during the collection. */ + if (vmem0 != vmem1 || vmem1 != vmem2) EVENT3(VMCompact, vmem0, vmem1, vmem2); - } + }); } mps_res_t mps_arena_vm_growth(mps_arena_t mps_arena, @@ -1181,8 +1199,7 @@ mps_res_t mps_arena_vm_growth(mps_arena_t mps_arena, ArenaEnter(arena); AVERT(Arena, arena); - vmArena = Arena2VMArena(arena); - AVERT(VMArena, vmArena); + vmArena = MustBeA(VMArena, arena); /* Must desire at least the minimum increment! */ AVER(desired >= minimum); @@ -1198,24 +1215,23 @@ mps_res_t mps_arena_vm_growth(mps_arena_t mps_arena, /* VMArenaClass -- The VM arena class definition */ -DEFINE_ARENA_CLASS(VMArenaClass, this) +DEFINE_CLASS(Arena, VMArena, klass) { - INHERIT_CLASS(this, AbstractArenaClass); - this->name = "VM"; - this->size = sizeof(VMArenaStruct); - this->offset = offsetof(VMArenaStruct, arenaStruct); - this->varargs = VMArenaVarargs; - this->init = VMArenaInit; - this->finish = VMArenaFinish; - this->purgeSpare = VMPurgeSpare; - this->grow = VMArenaGrow; - this->free = VMFree; - this->chunkInit = VMChunkInit; - this->chunkFinish = VMChunkFinish; - this->compact = VMCompact; - this->describe = VMArenaDescribe; - this->pagesMarkAllocated = VMPagesMarkAllocated; - AVERT(ArenaClass, this); + INHERIT_CLASS(klass, VMArena, AbstractArena); + klass->instClassStruct.describe = VMArenaDescribe; + klass->size = sizeof(VMArenaStruct); + klass->varargs = VMArenaVarargs; + klass->create = VMArenaCreate; + klass->destroy = VMArenaDestroy; + klass->purgeSpare = VMPurgeSpare; + klass->grow = VMArenaGrow; + klass->free = VMFree; + klass->chunkInit = VMChunkInit; + klass->chunkFinish = VMChunkFinish; + klass->compact = VMCompact; + klass->pagesMarkAllocated = VMPagesMarkAllocated; + klass->chunkPageMapped = VMChunkPageMapped; + AVERT(ArenaClass, klass); } @@ -1223,13 +1239,13 @@ DEFINE_ARENA_CLASS(VMArenaClass, this) mps_arena_class_t mps_arena_class_vm(void) { - return (mps_arena_class_t)VMArenaClassGet(); + return (mps_arena_class_t)CLASS(VMArena); } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/arg.c b/mps/code/arg.c index 4721c415ccb..8d129534a70 100644 --- a/mps/code/arg.c +++ b/mps/code/arg.c @@ -1,7 +1,7 @@ /* arg.c: ARGUMENT LISTS * * $Id$ - * Copyright (c) 2013-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2013-2018 Ravenbrook Limited. See end of file for license. * * .source: See . */ @@ -20,86 +20,102 @@ SRCID(arg, "$Id$"); * that don't have any meaningful checking they can do. */ -Bool ArgCheckCant(Arg arg) { +Bool ArgCheckCant(Arg arg) +{ UNUSED(arg); return TRUE; } -static Bool ArgCheckShouldnt(Arg arg) { +static Bool ArgCheckShouldnt(Arg arg) +{ UNUSED(arg); NOTREACHED; return FALSE; } -Bool ArgCheckFormat(Arg arg) { +Bool ArgCheckFormat(Arg arg) +{ CHECKD(Format, arg->val.format); return TRUE; } -Bool ArgCheckChain(Arg arg) { +Bool ArgCheckChain(Arg arg) +{ CHECKD(Chain, arg->val.chain); return TRUE; } -Bool ArgCheckSize(Arg arg) { +Bool ArgCheckSize(Arg arg) +{ UNUSED(arg); /* TODO: Add and call SizeCheck */ return TRUE; } -Bool ArgCheckAddr(Arg arg) { +Bool ArgCheckAddr(Arg arg) +{ UNUSED(arg); /* TODO: Add and call AddrCheck */ return TRUE; } -Bool ArgCheckPoolDebugOptions(Arg arg) { +Bool ArgCheckPoolDebugOptions(Arg arg) +{ CHECKD_NOSIG(PoolDebugOptions, (PoolDebugOptions)arg->val.pool_debug_options); return TRUE; } -Bool ArgCheckFun(Arg arg) { +Bool ArgCheckFun(Arg arg) +{ CHECKL(FUNCHECK(arg->val.addr_method)); /* FIXME: Potential pun here */ return TRUE; } -Bool ArgCheckAlign(Arg arg) { +Bool ArgCheckAlign(Arg arg) +{ CHECKL(AlignCheck(arg->val.align)); return TRUE; } -Bool ArgCheckBool(Arg arg) { +Bool ArgCheckBool(Arg arg) +{ CHECKL(BoolCheck(arg->val.b)); return TRUE; } -Bool ArgCheckCount(Arg arg) { +Bool ArgCheckCount(Arg arg) +{ UNUSED(arg); /* TODO: Add and call CountCheck */ return TRUE; } -Bool ArgCheckPointer(Arg arg) { +Bool ArgCheckPointer(Arg arg) +{ CHECKL(arg != NULL); return TRUE; } -Bool ArgCheckRankSet(Arg arg) { +Bool ArgCheckRankSet(Arg arg) +{ CHECKL(COMPATTYPE(RankSet, unsigned)); CHECKL(RankSetCheck(arg->val.u)); return TRUE; } -Bool ArgCheckRank(Arg arg) { +Bool ArgCheckRank(Arg arg) +{ CHECKL(RankCheck(arg->val.rank)); return TRUE; } -Bool ArgCheckdouble(Arg arg) { - /* It would be nice if we could check doubles with C89, but - it doesn't have isfinite() etc. which are in C99. */ +Bool ArgCheckdouble(Arg arg) +{ + /* Don't call isfinite() here because it's not in C89, and because + infinity is a valid value for MPS_KEY_PAUSE_TIME. */ UNUSED(arg); return TRUE; } -Bool ArgCheckPool(Arg arg) { +Bool ArgCheckPool(Arg arg) +{ CHECKD(Pool, arg->val.pool); return TRUE; } @@ -146,7 +162,8 @@ Bool ArgListCheck(ArgList args) /* ArgPick -- try to pick an argument out of the argument list by keyword */ -Bool ArgPick(ArgStruct *argOut, ArgList args, Key key) { +Bool ArgPick(ArgStruct *argOut, ArgList args, Key key) +{ Index i; AVER(argOut != NULL); @@ -173,7 +190,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) { +void ArgRequire(ArgStruct *argOut, ArgList args, Key key) +{ Bool b = ArgPick(argOut, args, key); ASSERT(b, key->name); } @@ -192,7 +210,7 @@ void ArgTrivVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/awlut.c b/mps/code/awlut.c index e31e157c14c..b0a71ab7454 100644 --- a/mps/code/awlut.c +++ b/mps/code/awlut.c @@ -13,6 +13,7 @@ #include "mpsavm.h" #include "fmtdy.h" #include "testlib.h" +#include "testthr.h" #include "mpslib.h" #include "mps.h" #include "mpstd.h" @@ -172,42 +173,79 @@ static void table_link(mps_word_t *t1, mps_word_t *t2) } -static void test(mps_arena_t arena, - mps_ap_t leafap, mps_ap_t exactap, mps_ap_t weakap, - mps_ap_t bogusap) -{ +typedef struct tables_s { + mps_arena_t arena; mps_word_t *weaktable; mps_word_t *exacttable; mps_word_t *preserve[TABLE_SLOTS]; /* preserves objects in the weak */ /* table by referring to them */ - size_t i, j; - void *p; + mps_ap_t weakap, exactap, bogusap, leafap; +} tables_s, *tables_t; - exacttable = alloc_table(TABLE_SLOTS, exactap); - weaktable = alloc_table(TABLE_SLOTS, weakap); - table_link(exacttable, weaktable); + +/* populate -- populate the weak table in a thread + * + * We use a thread to populate the table to avoid leaving any + * references to objects in the table in registers, so that we can + * test their weakness properly. + */ + +static void *populate(void *state) +{ + tables_t tables = state; + size_t i; + mps_thr_t me; + mps_root_t root; + + die(mps_thread_reg(&me, tables->arena), "mps_thread_reg(populate)"); + die(mps_root_create_thread(&root, tables->arena, me, &state), "mps_root_create_thread(populate)"); + + tables->exacttable = alloc_table(TABLE_SLOTS, tables->exactap); + tables->weaktable = alloc_table(TABLE_SLOTS, tables->weakap); + table_link(tables->exacttable, tables->weaktable); + + for(i = 0; i < TABLE_SLOTS; ++i) { + mps_word_t *string; + if (rnd() % 2 == 0) { + string = alloc_string("iamalive", tables->leafap); + tables->preserve[i] = string; + } else { + string = alloc_string("iamdead", tables->leafap); + tables->preserve[i] = 0; + } + set_table_slot(tables->weaktable, i, string); + string = alloc_string("iamexact", tables->leafap); + set_table_slot(tables->exacttable, i, string); + } + + mps_root_destroy(root); + mps_thread_dereg(me); + + return NULL; +} + +static void test(mps_arena_t arena, + mps_ap_t leafap, mps_ap_t exactap, mps_ap_t weakap, + mps_ap_t bogusap) +{ + tables_s tables; + size_t i, j; + testthr_t thr; + void *p; /* Leave bogusap between reserve and commit for the duration */ die(mps_reserve(&p, bogusap, 64), "Reserve bogus"); - for(i = 0; i < TABLE_SLOTS; ++i) { - mps_word_t *string; - /* 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 == 0 || i + 1 == TABLE_SLOTS) { - string = alloc_string("iamalive", leafap); - preserve[i] = string; - } else { - string = alloc_string("iamdead", leafap); - preserve[i] = 0; - } - set_table_slot(weaktable, i, string); - string = alloc_string("iamexact", leafap); - set_table_slot(exacttable, i, string); - } + tables.arena = arena; + tables.exactap = exactap; + tables.weakap = weakap; + tables.leafap = leafap; + tables.bogusap = bogusap; + + /* We using a thread for its pararallel execution, so just create + and wait for it to finish. */ + testthr_create(&thr, populate, &tables); + testthr_join(&thr, NULL); for(j = 0; j < ITERATIONS; ++j) { for(i = 0; i < TABLE_SLOTS; ++i) { @@ -219,12 +257,12 @@ static void test(mps_arena_t arena, mps_arena_release(arena); for(i = 0; i < TABLE_SLOTS; ++i) { - if (preserve[i] == 0) { - if (table_slot(weaktable, i)) { + if (tables.preserve[i] == 0) { + if (table_slot(tables.weaktable, i)) { error("Strongly unreachable weak table entry found, " "slot %"PRIuLONGEST".\n", (ulongest_t)i); } else { - if (table_slot(exacttable, i) != 0) { + if (table_slot(tables.exacttable, i) != 0) { error("Weak table entry deleted, but corresponding " "exact table entry not deleted, slot %"PRIuLONGEST".\n", (ulongest_t)i); diff --git a/mps/code/awluthe.c b/mps/code/awluthe.c index a6653a3e23b..f27613a75b0 100644 --- a/mps/code/awluthe.c +++ b/mps/code/awluthe.c @@ -14,6 +14,7 @@ #include "fmthe.h" #include "fmtdy.h" #include "testlib.h" +#include "testthr.h" #include "mpslib.h" #include "mps.h" #include "mpstd.h" @@ -177,42 +178,79 @@ static void table_link(mps_word_t *t1, mps_word_t *t2) } -static void test(mps_arena_t arena, - mps_ap_t leafap, mps_ap_t exactap, mps_ap_t weakap, - mps_ap_t bogusap) -{ +typedef struct tables_s { + mps_arena_t arena; mps_word_t *weaktable; mps_word_t *exacttable; mps_word_t *preserve[TABLE_SLOTS]; /* preserves objects in the weak */ /* table by referring to them */ - size_t i, j; - void *p; + mps_ap_t weakap, exactap, bogusap, leafap; +} tables_s, *tables_t; - exacttable = alloc_table(TABLE_SLOTS, exactap); - weaktable = alloc_table(TABLE_SLOTS, weakap); - table_link(exacttable, weaktable); +/* populate -- populate the weak table in a thread + * + * We use a thread to populate the table to avoid leaving any + * references to objects in the table in registers, so that we can + * test their weakness properly. + */ + +static void *populate(void *state) +{ + tables_t tables = state; + size_t i; + mps_thr_t me; + mps_root_t root; + + die(mps_thread_reg(&me, tables->arena), "mps_thread_reg(populate)"); + die(mps_root_create_thread(&root, tables->arena, me, &state), "mps_root_create_thread(populate)"); + + tables->exacttable = alloc_table(TABLE_SLOTS, tables->exactap); + tables->weaktable = alloc_table(TABLE_SLOTS, tables->weakap); + table_link(tables->exacttable, tables->weaktable); + + for(i = 0; i < TABLE_SLOTS; ++i) { + mps_word_t *string; + if (rnd() % 2 == 0) { + string = alloc_string("iamalive", tables->leafap); + tables->preserve[i] = string; + } else { + string = alloc_string("iamdead", tables->leafap); + tables->preserve[i] = 0; + } + set_table_slot(tables->weaktable, i, string); + string = alloc_string("iamexact", tables->leafap); + set_table_slot(tables->exacttable, i, string); + } + + mps_root_destroy(root); + mps_thread_dereg(me); + + return NULL; +} + +static void test(mps_arena_t arena, + mps_ap_t leafap, mps_ap_t exactap, mps_ap_t weakap, + mps_ap_t bogusap) +{ + tables_s tables; + size_t i, j; + testthr_t thr; + void *p; /* Leave bogusap between reserve and commit for the duration */ die(mps_reserve(&p, bogusap, 64), "Reserve bogus"); - 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). - */ - if (rnd() % 2 == 0 || i + 1 == TABLE_SLOTS) { - string = alloc_string("iamalive", leafap); - preserve[i] = string; - } else { - string = alloc_string("iamdead", leafap); - preserve[i] = 0; - } - set_table_slot(weaktable, i, string); - string = alloc_string("iamexact", leafap); - set_table_slot(exacttable, i, string); - } + tables.arena = arena; + tables.exactap = exactap; + tables.weakap = weakap; + tables.leafap = leafap; + tables.bogusap = bogusap; + /* We using a thread for its pararallel execution, so just create + and wait for it to finish. */ + testthr_create(&thr, populate, &tables); + testthr_join(&thr, NULL); + for(j = 0; j < ITERATIONS; ++j) { for(i = 0; i < TABLE_SLOTS; ++i) { (void)alloc_string("spong", leafap); @@ -223,12 +261,12 @@ static void test(mps_arena_t arena, mps_arena_release(arena); for(i = 0; i < TABLE_SLOTS; ++i) { - if (preserve[i] == 0) { - if (table_slot(weaktable, i)) { + if (tables.preserve[i] == 0) { + if (table_slot(tables.weaktable, i)) { error("Strongly unreachable weak table entry found, " "slot %"PRIuLONGEST".\n", (ulongest_t)i); } else { - if (table_slot(exacttable, i) != 0) { + if (table_slot(tables.exacttable, i) != 0) { error("Weak table entry deleted, but corresponding " "exact table entry not deleted, slot %"PRIuLONGEST".\n", (ulongest_t)i); diff --git a/mps/code/bt.c b/mps/code/bt.c index 1a79b82f5f3..844846ba723 100644 --- a/mps/code/bt.c +++ b/mps/code/bt.c @@ -191,8 +191,7 @@ Res BTCreate(BT *btReturn, Arena arena, Count length) AVERT(Arena, arena); AVER(length > 0); - res = ControlAlloc(&p, arena, BTSize(length), - /* withReservoirPermit */ FALSE); + res = ControlAlloc(&p, arena, BTSize(length)); if (res != ResOK) return res; bt = (BT)p; diff --git a/mps/code/bttest.c b/mps/code/bttest.c index 20bb4cab4e7..cc51b9cf63d 100644 --- a/mps/code/bttest.c +++ b/mps/code/bttest.c @@ -1,7 +1,7 @@ /* bttest.c: BIT TABLE TEST * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. */ @@ -311,7 +311,8 @@ static void obeyCommand(const char *command) } -static void showBT(void) { +static void showBT(void) +{ Index i; char c; if (bt == NULL) @@ -350,7 +351,7 @@ static void showBT(void) { #define testArenaSIZE (((size_t)64)<<20) -extern int main(int argc, char *argv[]) +int main(int argc, char *argv[]) { bt = NULL; btSize = 0; @@ -376,7 +377,7 @@ extern int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/buffer.c b/mps/code/buffer.c index edef611c217..1fe68becfac 100644 --- a/mps/code/buffer.c +++ b/mps/code/buffer.c @@ -1,7 +1,7 @@ /* buffer.c: ALLOCATION BUFFER IMPLEMENTATION * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 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 @@ -28,10 +28,6 @@ SRCID(buffer, "$Id$"); -/* forward declarations */ -static void BufferFrameNotifyPopPending(Buffer buffer); - - /* BufferCheck -- check consistency of a buffer * * See .ap.async. */ @@ -39,6 +35,7 @@ static void BufferFrameNotifyPopPending(Buffer buffer); Bool BufferCheck(Buffer buffer) { CHECKS(Buffer, buffer); + CHECKC(Buffer, buffer); CHECKL(buffer->serial < buffer->pool->bufferSerial); /* .trans.mod */ CHECKU(Arena, buffer->arena); CHECKU(Pool, buffer->pool); @@ -50,15 +47,6 @@ Bool BufferCheck(Buffer buffer) CHECKL(buffer->emptySize <= buffer->fillSize); CHECKL(buffer->alignment == buffer->pool->alignment); CHECKL(AlignCheck(buffer->alignment)); - CHECKL(BoolCheck(buffer->ap_s._enabled)); - - if (buffer->ap_s._enabled) { - /* no useful check for frameptr - mutator may be updating it */ - CHECKL(BoolCheck(buffer->ap_s._lwpoppending)); - } else { - CHECKL(buffer->ap_s._lwpoppending == FALSE); - CHECKL(buffer->ap_s._frameptr == NULL); - } /* If any of the buffer's fields indicate that it is reset, make */ /* sure it is really reset. Otherwise, check various properties */ @@ -79,8 +67,6 @@ Bool BufferCheck(Buffer buffer) /* Nothing reliable to check for lightweight frame state */ CHECKL(buffer->poolLimit == (Addr)0); } else { - Addr aplimit; - /* The buffer is attached to a region of memory. */ /* Check consistency. */ CHECKL(buffer->mode & BufferModeATTACHED); @@ -99,14 +85,6 @@ Bool BufferCheck(Buffer buffer) CHECKL(AddrIsAligned(buffer->ap_s.limit, buffer->alignment)); CHECKL(AddrIsAligned(buffer->poolLimit, buffer->alignment)); - /* .lwcheck: If LW frames are enabled, the buffer may become */ - /* trapped asynchronously. It can't become untrapped */ - /* asynchronously, though. See . */ - /* Read a snapshot value of the limit field. Use this to determine */ - /* if we are trapped, and to permit more useful checking when not */ - /* yet trapped. */ - aplimit = buffer->ap_s.limit; - /* If the buffer isn't trapped then "limit" should be the limit */ /* set by the owning pool. Otherwise, "init" is either at the */ /* same place it was at flip (.commit.before) or has been set */ @@ -117,12 +95,10 @@ Bool BufferCheck(Buffer buffer) /* request.dylan.170429.sol.zero_). */ /* .. _request.dylan.170429.sol.zero: https://info.ravenbrook.com/project/mps/import/2001-11-05/mmprevol/request/dylan/170429 */ - if ((buffer->ap_s._enabled && aplimit == (Addr)0) /* see .lwcheck */ - || (!buffer->ap_s._enabled && BufferIsTrapped(buffer))) { + if (BufferIsTrapped(buffer)) { /* .check.use-trapped: This checking function uses BufferIsTrapped, */ /* So BufferIsTrapped can't do checking as that would cause an */ /* infinite loop. */ - CHECKL(aplimit == (Addr)0); if (buffer->mode & BufferModeFLIPPED) { CHECKL(buffer->ap_s.init == buffer->initAtFlip || buffer->ap_s.init == buffer->ap_s.alloc); @@ -131,7 +107,6 @@ Bool BufferCheck(Buffer buffer) } /* Nothing special to check in the logged mode. */ } else { - CHECKL(aplimit == buffer->poolLimit); /* see .lwcheck */ CHECKL(buffer->initAtFlip == (Addr)0); } } @@ -144,71 +119,69 @@ Bool BufferCheck(Buffer buffer) * * See for structure definitions. */ -Res BufferDescribe(Buffer buffer, mps_lib_FILE *stream, Count depth) +static Res BufferAbsDescribe(Inst inst, mps_lib_FILE *stream, Count depth) { + Buffer buffer = CouldBeA(Buffer, inst); Res res; - if (!TESTT(Buffer, buffer)) - return ResFAIL; + if (!TESTC(Buffer, buffer)) + return ResPARAM; if (stream == NULL) - return ResFAIL; + return ResPARAM; - 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); + res = NextMethod(Inst, Buffer, describe)(inst, stream, depth); if (res != ResOK) return res; - res = buffer->class->describe(buffer, stream, depth + 2); - if (res != ResOK) - return res; + return WriteF(stream, depth + 2, + "serial $U\n", (WriteFU)buffer->serial, + "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); +} - res = WriteF(stream, depth, "} Buffer $P ($U)\n", - (WriteFP)buffer, (WriteFU)buffer->serial, - NULL); - return res; +Res BufferDescribe(Buffer buffer, mps_lib_FILE *stream, Count depth) +{ + return Method(Inst, buffer, describe)(MustBeA(Inst, buffer), stream, depth); } /* BufferInit -- initialize an allocation buffer */ -static Res BufferInit(Buffer buffer, BufferClass class, - Pool pool, Bool isMutator, ArgList args) +static Res BufferAbsInit(Buffer buffer, Pool pool, Bool isMutator, ArgList args) { Arena arena; - Res res; AVER(buffer != NULL); - AVERT(BufferClass, class); AVERT(Pool, pool); - + AVER(BoolCheck(isMutator)); + AVERT(ArgList, args); + + /* Superclass init */ + InstInit(CouldBeA(Inst, buffer)); + arena = PoolArena(pool); - /* Initialize the buffer. See for a definition of */ - /* the structure. sig and serial comes later .init.sig-serial */ + + /* Initialize the buffer. See for a definition of + the structure. sig and serial comes later .init.sig-serial */ buffer->arena = arena; - buffer->class = class; buffer->pool = pool; RingInit(&buffer->poolRing); buffer->isMutator = isMutator; @@ -228,43 +201,40 @@ static Res BufferInit(Buffer buffer, BufferClass class, buffer->ap_s.init = (mps_addr_t)0; buffer->ap_s.alloc = (mps_addr_t)0; buffer->ap_s.limit = (mps_addr_t)0; - buffer->ap_s._frameptr = NULL; - buffer->ap_s._enabled = FALSE; - buffer->ap_s._lwpoppending = FALSE; buffer->poolLimit = (Addr)0; buffer->rampCount = 0; - /* .init.sig-serial: Now the vanilla stuff is initialized, */ - /* sign the buffer and give it a serial number. It can */ - /* then be safely checked in subclass methods. */ - buffer->sig = BufferSig; + /* .init.sig-serial: Now the vanilla stuff is initialized, sign the + buffer and give it a serial number. It can then be safely checked + in subclass methods. */ buffer->serial = pool->bufferSerial; /* .trans.mod */ ++pool->bufferSerial; + SetClassOfPoly(buffer, CLASS(Buffer)); + buffer->sig = BufferSig; AVERT(Buffer, buffer); - /* Dispatch to the buffer class method to perform any */ - /* class-specific initialization of the buffer. */ - res = (*class->init)(buffer, pool, args); - if (res != ResOK) - goto failInit; - /* Attach the initialized buffer to the pool. */ RingAppend(&pool->bufferRing, &buffer->poolRing); - return ResOK; + EVENT3(BufferInit, buffer, pool, BOOLOF(buffer->isMutator)); -failInit: - RingFinish(&buffer->poolRing); - buffer->sig = SigInvalid; - return res; + return ResOK; +} + +static Res BufferInit(Buffer buffer, BufferClass klass, + Pool pool, Bool isMutator, ArgList args) +{ + AVERT(BufferClass, klass); + return klass->init(buffer, pool, isMutator, args); } /* BufferCreate -- create an allocation buffer * - * See . */ + * See . + */ -Res BufferCreate(Buffer *bufferReturn, BufferClass class, +Res BufferCreate(Buffer *bufferReturn, BufferClass klass, Pool pool, Bool isMutator, ArgList args) { Res res; @@ -273,20 +243,19 @@ Res BufferCreate(Buffer *bufferReturn, BufferClass class, void *p; AVER(bufferReturn != NULL); - AVERT(BufferClass, class); + AVERT(BufferClass, klass); AVERT(Pool, pool); arena = PoolArena(pool); /* Allocate memory for the buffer descriptor structure. */ - res = ControlAlloc(&p, arena, class->size, - /* withReservoirPermit */ FALSE); + res = ControlAlloc(&p, arena, klass->size); if (res != ResOK) goto failAlloc; buffer = p; /* Initialize the buffer descriptor structure. */ - res = BufferInit(buffer, class, pool, isMutator, args); + res = BufferInit(buffer, klass, pool, isMutator, args); if (res != ResOK) goto failInit; @@ -294,7 +263,7 @@ Res BufferCreate(Buffer *bufferReturn, BufferClass class, return ResOK; failInit: - ControlFree(arena, buffer, class->size); + ControlFree(arena, buffer, klass->size); failAlloc: return res; } @@ -312,17 +281,16 @@ void BufferDetach(Buffer buffer, Pool pool) Size spare; buffer->mode |= BufferModeTRANSITION; - init = buffer->ap_s.init; - limit = buffer->poolLimit; + /* Ask the owning pool to do whatever it needs to before the */ /* buffer is detached (e.g. copy buffer state into pool state). */ - (*pool->class->bufferEmpty)(pool, buffer, init, limit); - /* Use of lightweight frames must have been disabled by now */ - AVER(BufferFrameState(buffer) == BufferFrameDISABLED); + Method(Pool, pool, bufferEmpty)(pool, buffer); /* run any class-specific detachment method */ - buffer->class->detach(buffer); + Method(Buffer, buffer, detach)(buffer); + init = BufferGetInit(buffer); + limit = BufferLimit(buffer); spare = AddrOffset(init, limit); buffer->emptySize += spare; if (buffer->isMutator) { @@ -342,7 +310,6 @@ void BufferDetach(Buffer buffer, Pool pool) buffer->poolLimit = (Addr)0; buffer->mode &= ~(BufferModeATTACHED|BufferModeFLIPPED|BufferModeTRANSITION); - BufferFrameSetState(buffer, BufferFrameDISABLED); EVENT2(BufferEmpty, buffer, spare); } @@ -356,42 +323,26 @@ void BufferDetach(Buffer buffer, Pool pool) void BufferDestroy(Buffer buffer) { Arena arena; - BufferClass class; - + Size size; AVERT(Buffer, buffer); arena = buffer->arena; - class = buffer->class; - AVERT(BufferClass, class); + size = ClassOfPoly(Buffer, buffer)->size; BufferFinish(buffer); - ControlFree(arena, buffer, class->size); + ControlFree(arena, buffer, size); } /* BufferFinish -- finish an allocation buffer */ -void BufferFinish(Buffer buffer) +static void BufferAbsFinish(Inst inst) { - Pool pool; - + Buffer buffer = MustBeA(Buffer, inst); AVERT(Buffer, buffer); - - pool = BufferPool(buffer); - - AVER(BufferIsReady(buffer)); - - /* */ - if (BufferIsTrappedByMutator(buffer)) { - BufferFrameNotifyPopPending(buffer); - } - - BufferDetach(buffer, pool); - - /* Dispatch to the buffer class method to perform any */ - /* class-specific finishing of the buffer. */ - (*buffer->class->finish)(buffer); + AVER(BufferIsReset(buffer)); /* Detach the buffer from its owning pool and unsig it. */ RingRemove(&buffer->poolRing); + InstFinish(MustBeA(Inst, buffer)); buffer->sig = SigInvalid; /* Finish off the generic buffer fields. */ @@ -400,6 +351,16 @@ void BufferFinish(Buffer buffer) EVENT1(BufferFinish, buffer); } +void BufferFinish(Buffer buffer) +{ + AVERT(Buffer, buffer); + AVER(BufferIsReady(buffer)); + + BufferDetach(buffer, BufferPool(buffer)); /* FIXME: Should be in BufferAbsFinish? */ + + Method(Inst, buffer, finish)(MustBeA(Inst, buffer)); +} + /* BufferIsReset -- test whether a buffer is in the "reset" state * @@ -459,44 +420,6 @@ static void BufferSetUnflipped(Buffer buffer) } -/* BufferFrameState - * - * Returns the frame state of a buffer. See - * . */ - -FrameState BufferFrameState(Buffer buffer) -{ - AVERT(Buffer, buffer); - if (buffer->ap_s._enabled) { - if (buffer->ap_s._lwpoppending) { - return BufferFramePOP_PENDING; - } else { - AVER(buffer->ap_s._frameptr == NULL); - return BufferFrameVALID; - } - } else { - AVER(buffer->ap_s._frameptr == NULL); - AVER(buffer->ap_s._lwpoppending == FALSE); - return BufferFrameDISABLED; - } -} - - -/* BufferFrameSetState - * - * Sets the frame state of a buffer. Only the mutator may set the - * PopPending state. See . */ - -void BufferFrameSetState(Buffer buffer, FrameState state) -{ - AVERT(Buffer, buffer); - AVER(state == BufferFrameVALID || state == BufferFrameDISABLED); - buffer->ap_s._frameptr = NULL; - buffer->ap_s._lwpoppending = FALSE; - buffer->ap_s._enabled = (state == BufferFrameVALID); -} - - /* BufferSetAllocAddr * * Sets the init & alloc pointers of a buffer. */ @@ -514,32 +437,6 @@ void BufferSetAllocAddr(Buffer buffer, Addr addr) } -/* BufferFrameNotifyPopPending - * - * Notifies the pool when a lightweight frame pop operation has been - * deferred and needs to be processed. See - * . */ - -static void BufferFrameNotifyPopPending(Buffer buffer) -{ - AllocFrame frame; - Pool pool; - AVER(BufferIsTrappedByMutator(buffer)); - AVER(BufferFrameState(buffer) == BufferFramePOP_PENDING); - frame = (AllocFrame)buffer->ap_s._frameptr; - /* Unset PopPending state & notify the pool */ - BufferFrameSetState(buffer, BufferFrameVALID); - /* If the frame is no longer trapped, undo the trap by resetting */ - /* the AP limit pointer */ - if (!BufferIsTrapped(buffer)) { - buffer->ap_s.limit = buffer->poolLimit; - } - pool = BufferPool(buffer); - (*pool->class->framePopPending)(pool, buffer, frame); -} - - - /* BufferFramePush * * See . */ @@ -551,20 +448,15 @@ Res BufferFramePush(AllocFrame *frameReturn, Buffer buffer) AVER(frameReturn != NULL); - /* Process any flip or PopPending */ + /* Process any flip */ if (!BufferIsReset(buffer) && buffer->ap_s.limit == (Addr)0) { /* .fill.unflip: If the buffer is flipped then we unflip the buffer. */ if (buffer->mode & BufferModeFLIPPED) { BufferSetUnflipped(buffer); } - - /* check for PopPending */ - if (BufferIsTrappedByMutator(buffer)) { - BufferFrameNotifyPopPending(buffer); - } } pool = BufferPool(buffer); - return (*pool->class->framePush)(frameReturn, pool, buffer); + return Method(Pool, pool, framePush)(frameReturn, pool, buffer); } @@ -578,7 +470,7 @@ Res BufferFramePop(Buffer buffer, AllocFrame frame) AVERT(Buffer, buffer); /* frame is of an abstract type & can't be checked */ pool = BufferPool(buffer); - return (*pool->class->framePop)(pool, buffer, frame); + return Method(Pool, pool, framePop)(pool, buffer, frame); } @@ -588,8 +480,7 @@ Res BufferFramePop(Buffer buffer, AllocFrame frame) * * .reserve: Keep in sync with . */ -Res BufferReserve(Addr *pReturn, Buffer buffer, Size size, - Bool withReservoirPermit) +Res BufferReserve(Addr *pReturn, Buffer buffer, Size size) { Addr next; @@ -597,8 +488,7 @@ Res BufferReserve(Addr *pReturn, Buffer buffer, Size size, AVERT(Buffer, buffer); AVER(size > 0); AVER(SizeIsAligned(size, BufferPool(buffer)->alignment)); - AVER(BufferIsReady(buffer)); - AVERT(Bool, withReservoirPermit); + AVER(BufferIsReady(buffer)); /* */ /* Is there enough room in the unallocated portion of the buffer to */ /* satisfy the request? If so, just increase the alloc marker and */ @@ -612,7 +502,7 @@ Res BufferReserve(Addr *pReturn, Buffer buffer, Size size, } /* If the buffer can't accommodate the request, call "fill". */ - return BufferFill(pReturn, buffer, size, withReservoirPermit); + return BufferFill(pReturn, buffer, size); } @@ -659,7 +549,7 @@ void BufferAttach(Buffer buffer, Addr base, Addr limit, } /* run any class-specific attachment method */ - buffer->class->attach(buffer, base, limit, init, size); + Method(Buffer, buffer, attach)(buffer, base, limit, init, size); AVERT(Buffer, buffer); EVENT4(BufferFill, buffer, size, base, filled); @@ -673,8 +563,7 @@ void BufferAttach(Buffer buffer, Addr base, Addr limit, * allocation request. This might be because the buffer has been * trapped and "limit" has been set to zero. */ -Res BufferFill(Addr *pReturn, Buffer buffer, Size size, - Bool withReservoirPermit) +Res BufferFill(Addr *pReturn, Buffer buffer, Size size) { Res res; Pool pool; @@ -696,11 +585,6 @@ Res BufferFill(Addr *pReturn, Buffer buffer, Size size, BufferSetUnflipped(buffer); } - /* */ - if (BufferIsTrappedByMutator(buffer)) { - BufferFrameNotifyPopPending(buffer); - } - /* .fill.logged: If the buffer is logged then we leave it logged. */ next = AddrAdd(buffer->ap_s.alloc, size); if (next > (Addr)buffer->ap_s.alloc && @@ -721,9 +605,7 @@ Res BufferFill(Addr *pReturn, Buffer buffer, Size size, BufferDetach(buffer, pool); /* Ask the pool for some memory. */ - res = (*pool->class->bufferFill)(&base, &limit, - pool, buffer, size, - withReservoirPermit); + res = Method(Pool, pool, bufferFill)(&base, &limit, pool, buffer, size); if (res != ResOK) return res; @@ -801,8 +683,6 @@ Bool BufferTrip(Buffer buffer, Addr p, Size size) AVER(buffer->ap_s.limit == 0); /* Of course we should be trapped. */ AVER(BufferIsTrapped(buffer)); - /* But the mutator shouldn't have caused the trap */ - AVER(!BufferIsTrappedByMutator(buffer)); /* The init and alloc fields should be equal at this point, because */ /* the step .commit.update has happened. */ @@ -846,7 +726,7 @@ Bool BufferTrip(Buffer buffer, Addr p, Size size) b = PoolFormat(&format, buffer->pool); if (b) { - clientClass = format->class(p); + clientClass = format->klass(p); } else { clientClass = (Addr)0; } @@ -907,21 +787,21 @@ Addr BufferScanLimit(Buffer buffer) Seg BufferSeg(Buffer buffer) { AVERT(Buffer, buffer); - return buffer->class->seg(buffer); + return Method(Buffer, buffer, seg)(buffer); } RankSet BufferRankSet(Buffer buffer) { AVERT(Buffer, buffer); - return buffer->class->rankSet(buffer); + return Method(Buffer, buffer, rankSet)(buffer); } void BufferSetRankSet(Buffer buffer, RankSet rankset) { AVERT(Buffer, buffer); AVERT(RankSet, rankset); - buffer->class->setRankSet(buffer, rankset); + Method(Buffer, buffer, setRankSet)(buffer, rankset); } @@ -937,7 +817,7 @@ void BufferReassignSeg(Buffer buffer, Seg seg) AVER(BufferBase(buffer) >= SegBase(seg)); AVER(BufferLimit(buffer) <= SegLimit(seg)); AVER(BufferPool(buffer) == SegPool(seg)); - buffer->class->reassignSeg(buffer, seg); + Method(Buffer, buffer, reassignSeg)(buffer, seg); } @@ -949,21 +829,7 @@ void BufferReassignSeg(Buffer buffer, Seg seg) Bool BufferIsTrapped(Buffer buffer) { /* Can't check buffer, see .check.use-trapped */ - return BufferIsTrappedByMutator(buffer) - || ((buffer->mode & (BufferModeFLIPPED|BufferModeLOGGED)) != 0); -} - - -/* BufferIsTrappedByMutator - * - * Indicates whether the mutator trapped the buffer. See - * and .ap.async. */ - -Bool BufferIsTrappedByMutator(Buffer buffer) -{ - AVER(!buffer->ap_s._lwpoppending || buffer->ap_s._enabled); - /* Can't check buffer, see .check.use-trapped */ - return buffer->ap_s._lwpoppending; + return (buffer->mode & (BufferModeFLIPPED|BufferModeLOGGED)) != 0; } @@ -1012,7 +878,7 @@ void BufferRampBegin(Buffer buffer, AllocPattern pattern) pool = BufferPool(buffer); AVERT(Pool, pool); - (*pool->class->rampBegin)(pool, buffer, + Method(Pool, pool, rampBegin)(pool, buffer, pattern == &AllocPatternRampCollectAllStruct); } @@ -1031,7 +897,7 @@ Res BufferRampEnd(Buffer buffer) pool = BufferPool(buffer); AVERT(Pool, pool); - (*pool->class->rampEnd)(pool, buffer); + Method(Pool, pool, rampEnd)(pool, buffer); return ResOK; } @@ -1050,7 +916,7 @@ void BufferRampReset(Buffer buffer) pool = BufferPool(buffer); AVERT(Pool, pool); do - (*pool->class->rampEnd)(pool, buffer); + Method(Pool, pool, rampEnd)(pool, buffer); while(--buffer->rampCount > 0); } @@ -1059,30 +925,6 @@ void BufferRampReset(Buffer buffer) /* BufferClass -- support for the basic Buffer class */ -/* bufferTrivInit -- basic buffer init method */ - -static Res bufferTrivInit(Buffer buffer, Pool pool, ArgList args) -{ - /* initialization happens in BufferInit so checks are safe */ - AVERT(Buffer, buffer); - AVERT(Pool, pool); - UNUSED(args); - EVENT3(BufferInit, buffer, pool, BOOLOF(buffer->isMutator)); - return ResOK; -} - - -/* bufferTrivFinish -- basic buffer finish method */ - -static void bufferTrivFinish(Buffer buffer) -{ - /* No special finish for simple buffers */ - AVERT(Buffer, buffer); - AVER(BufferIsReset(buffer)); - NOOP; -} - - /* bufferTrivAttach -- basic buffer attach method */ static void bufferTrivAttach(Buffer buffer, Addr base, Addr limit, @@ -1159,38 +1001,28 @@ static void bufferNoReassignSeg(Buffer buffer, Seg seg) } -/* bufferTrivDescribe -- basic Buffer describe method */ - -static Res bufferTrivDescribe(Buffer buffer, mps_lib_FILE *stream, Count depth) -{ - if (!TESTT(Buffer, buffer)) - return ResFAIL; - if (stream == NULL) - return ResFAIL; - UNUSED(depth); - /* dispatching function does it all */ - return ResOK; -} - - /* BufferClassCheck -- check the consistency of a BufferClass */ -Bool BufferClassCheck(BufferClass class) +Bool BufferClassCheck(BufferClass klass) { - CHECKD(ProtocolClass, &class->protocol); - CHECKL(class->name != NULL); /* Should be <=6 char C identifier */ - CHECKL(class->size >= sizeof(BufferStruct)); - CHECKL(FUNCHECK(class->varargs)); - CHECKL(FUNCHECK(class->init)); - CHECKL(FUNCHECK(class->finish)); - CHECKL(FUNCHECK(class->attach)); - CHECKL(FUNCHECK(class->detach)); - CHECKL(FUNCHECK(class->seg)); - CHECKL(FUNCHECK(class->rankSet)); - CHECKL(FUNCHECK(class->setRankSet)); - CHECKL(FUNCHECK(class->reassignSeg)); - CHECKL(FUNCHECK(class->describe)); - CHECKS(BufferClass, class); + CHECKD(InstClass, &klass->instClassStruct); + CHECKL(klass->size >= sizeof(BufferStruct)); + CHECKL(FUNCHECK(klass->varargs)); + CHECKL(FUNCHECK(klass->init)); + CHECKL(FUNCHECK(klass->attach)); + CHECKL(FUNCHECK(klass->detach)); + CHECKL(FUNCHECK(klass->seg)); + CHECKL(FUNCHECK(klass->rankSet)); + CHECKL(FUNCHECK(klass->setRankSet)); + CHECKL(FUNCHECK(klass->reassignSeg)); + + /* Check that buffer classes override sets of related methods. */ + CHECKL((klass->init == BufferAbsInit) + == (klass->instClassStruct.finish == BufferAbsFinish)); + CHECKL((klass->attach == bufferTrivAttach) + == (klass->detach == bufferTrivDetach)); + + CHECKS(BufferClass, klass); return TRUE; } @@ -1199,23 +1031,28 @@ Bool BufferClassCheck(BufferClass class) * * See . */ -DEFINE_CLASS(BufferClass, class) +DEFINE_CLASS(Inst, BufferClass, klass) { - INHERIT_CLASS(&class->protocol, ProtocolClass); - class->name = "BUFFER"; - class->size = sizeof(BufferStruct); - class->varargs = ArgTrivVarargs; - class->init = bufferTrivInit; - class->finish = bufferTrivFinish; - class->attach = bufferTrivAttach; - class->detach = bufferTrivDetach; - class->describe = bufferTrivDescribe; - class->seg = bufferNoSeg; - class->rankSet = bufferTrivRankSet; - class->setRankSet = bufferNoSetRankSet; - class->reassignSeg = bufferNoReassignSeg; - class->sig = BufferClassSig; - AVERT(BufferClass, class); + INHERIT_CLASS(klass, BufferClass, InstClass); + AVERT(InstClass, klass); +} + +DEFINE_CLASS(Buffer, Buffer, klass) +{ + INHERIT_CLASS(&klass->instClassStruct, Buffer, Inst); + klass->instClassStruct.finish = BufferAbsFinish; + klass->instClassStruct.describe = BufferAbsDescribe; + klass->size = sizeof(BufferStruct); + klass->varargs = ArgTrivVarargs; + klass->init = BufferAbsInit; + klass->attach = bufferTrivAttach; + klass->detach = bufferTrivDetach; + klass->seg = bufferNoSeg; + klass->rankSet = bufferTrivRankSet; + klass->setRankSet = bufferNoSetRankSet; + klass->reassignSeg = bufferNoReassignSeg; + klass->sig = BufferClassSig; + AVERT(BufferClass, klass); } @@ -1223,19 +1060,13 @@ DEFINE_CLASS(BufferClass, class) /* SegBufClass -- support for the SegBuf subclass */ -/* BufferSegBuf -- convert generic Buffer to a SegBuf */ - -#define BufferSegBuf(buffer) ((SegBuf)(buffer)) - - /* SegBufCheck -- check consistency of a SegBuf */ Bool SegBufCheck(SegBuf segbuf) { Buffer buffer; - CHECKS(SegBuf, segbuf); - buffer = &segbuf->bufferStruct; + buffer = MustBeA(Buffer, segbuf); CHECKD(Buffer, buffer); CHECKL(RankSetCheck(segbuf->rankSet)); @@ -1262,27 +1093,24 @@ Bool SegBufCheck(SegBuf segbuf) /* segBufInit -- SegBuf init method */ -static Res segBufInit(Buffer buffer, Pool pool, ArgList args) +static Res segBufInit(Buffer buffer, Pool pool, Bool isMutator, ArgList args) { - BufferClass super; SegBuf segbuf; Res res; - AVERT(Buffer, buffer); - AVERT(Pool, pool); - segbuf = BufferSegBuf(buffer); - /* Initialize the superclass fields first via next-method call */ - super = BUFFER_SUPERCLASS(SegBufClass); - res = super->init(buffer, pool, args); + res = NextMethod(Buffer, SegBuf, init)(buffer, pool, isMutator, args); if (res != ResOK) return res; + segbuf = CouldBeA(SegBuf, buffer); segbuf->seg = NULL; - segbuf->sig = SegBufSig; segbuf->rankSet = RankSetEMPTY; - - AVERT(SegBuf, segbuf); + + SetClassOfPoly(buffer, CLASS(SegBuf)); + segbuf->sig = SegBufSig; + AVERC(SegBuf, segbuf); + EVENT3(BufferInitSeg, buffer, pool, BOOLOF(buffer->isMutator)); return ResOK; } @@ -1290,21 +1118,13 @@ static Res segBufInit(Buffer buffer, Pool pool, ArgList args) /* segBufFinish -- SegBuf finish method */ -static void segBufFinish (Buffer buffer) +static void segBufFinish(Inst inst) { - BufferClass super; - SegBuf segbuf; - - AVERT(Buffer, buffer); + Buffer buffer = MustBeA(Buffer, inst); + SegBuf segbuf = MustBeA(SegBuf, buffer); AVER(BufferIsReset(buffer)); - segbuf = BufferSegBuf(buffer); - AVERT(SegBuf, segbuf); - segbuf->sig = SigInvalid; - - /* finish the superclass fields last */ - super = BUFFER_SUPERCLASS(SegBufClass); - super->finish(buffer); + NextMethod(Inst, SegBuf, finish)(inst); } @@ -1313,22 +1133,20 @@ static void segBufFinish (Buffer buffer) static void segBufAttach(Buffer buffer, Addr base, Addr limit, Addr init, Size size) { - SegBuf segbuf; + SegBuf segbuf = MustBeA(SegBuf, buffer); Seg seg = NULL; /* suppress "may be used uninitialized" */ Arena arena; Bool found; - AVERT(Buffer, buffer); /* Other parameters are consistency checked in BufferAttach */ UNUSED(init); UNUSED(size); - segbuf = BufferSegBuf(buffer); arena = BufferArena(buffer); found = SegOfAddr(&seg, arena, base); AVER(found); AVER(segbuf->seg == NULL); - AVER(SegBuffer(seg) == NULL); + AVER(!SegHasBuffer(seg)); AVER(SegBase(seg) <= base); AVER(limit <= SegLimit(seg)); @@ -1344,56 +1162,37 @@ static void segBufAttach(Buffer buffer, Addr base, Addr limit, static void segBufDetach(Buffer buffer) { - SegBuf segbuf; - Seg seg; - - AVERT(Buffer, buffer); - segbuf = BufferSegBuf(buffer); - AVERT(SegBuf, segbuf); - - seg = segbuf->seg; - AVER(seg != NULL); - SegSetBuffer(seg, NULL); + SegBuf segbuf = MustBeA(SegBuf, buffer); + Seg seg = segbuf->seg; + SegUnsetBuffer(seg); segbuf->seg = NULL; } /* segBufSeg -- BufferSeg accessor method for SegBuf instances */ -static Seg segBufSeg (Buffer buffer) +static Seg segBufSeg(Buffer buffer) { - SegBuf segbuf; - - AVERT(Buffer, buffer); - segbuf = BufferSegBuf(buffer); - AVERT(SegBuf, segbuf); + SegBuf segbuf = MustBeA(SegBuf, buffer); return segbuf->seg; } /* segBufRankSet -- BufferRankSet accessor for SegBuf instances */ -static RankSet segBufRankSet (Buffer buffer) +static RankSet segBufRankSet(Buffer buffer) { - SegBuf segbuf; - - AVERT(Buffer, buffer); - segbuf = BufferSegBuf(buffer); - AVERT(SegBuf, segbuf); + SegBuf segbuf = MustBeA(SegBuf, buffer); return segbuf->rankSet; } /* segBufSetRankSet -- BufferSetRankSet setter method for SegBuf */ -static void segBufSetRankSet (Buffer buffer, RankSet rankset) +static void segBufSetRankSet(Buffer buffer, RankSet rankset) { - SegBuf segbuf; - - AVERT(Buffer, buffer); + SegBuf segbuf = MustBeA(SegBuf, buffer); AVERT(RankSet, rankset); - segbuf = BufferSegBuf(buffer); - AVERT(SegBuf, segbuf); segbuf->rankSet = rankset; } @@ -1405,13 +1204,10 @@ static void segBufSetRankSet (Buffer buffer, RankSet rankset) * .invseg: On entry the buffer is attached to an invalid segment, which * can't be checked. The method is called to make the attachment valid. */ -static void segBufReassignSeg (Buffer buffer, Seg seg) +static void segBufReassignSeg(Buffer buffer, Seg seg) { - SegBuf segbuf; - - AVERT(Buffer, buffer); + SegBuf segbuf = CouldBeA(SegBuf, buffer); AVERT(Seg, seg); - segbuf = BufferSegBuf(buffer); /* Can't check segbuf on entry. See .invseg */ AVER(NULL != segbuf->seg); AVER(seg != segbuf->seg); @@ -1422,32 +1218,25 @@ static void segBufReassignSeg (Buffer buffer, Seg seg) /* segBufDescribe -- describe method for SegBuf */ -static Res segBufDescribe(Buffer buffer, mps_lib_FILE *stream, Count depth) +static Res segBufDescribe(Inst inst, mps_lib_FILE *stream, Count depth) { - SegBuf segbuf; - BufferClass super; + Buffer buffer = CouldBeA(Buffer, inst); + SegBuf segbuf = CouldBeA(SegBuf, buffer); Res res; - if (!TESTT(Buffer, buffer)) - return ResFAIL; + if (!TESTC(SegBuf, segbuf)) + return ResPARAM; if (stream == NULL) - return ResFAIL; - segbuf = BufferSegBuf(buffer); - if (!TESTT(SegBuf, segbuf)) - return ResFAIL; + return ResPARAM; - /* Describe the superclass fields first via next-method call */ - super = BUFFER_SUPERCLASS(SegBufClass); - res = super->describe(buffer, stream, depth); + res = NextMethod(Inst, SegBuf, describe)(inst, stream, depth); if (res != ResOK) return res; - res = WriteF(stream, depth, - "Seg $P\n", (WriteFP)segbuf->seg, - "rankSet $U\n", (WriteFU)segbuf->rankSet, - NULL); - - return res; + return WriteF(stream, depth + 2, + "Seg $P\n", (WriteFP)segbuf->seg, + "rankSet $U\n", (WriteFU)segbuf->rankSet, + NULL); } @@ -1456,23 +1245,20 @@ static Res segBufDescribe(Buffer buffer, mps_lib_FILE *stream, Count depth) * Supports an association with a single segment when attached. See * . */ -typedef BufferClassStruct SegBufClassStruct; - -DEFINE_CLASS(SegBufClass, class) +DEFINE_CLASS(Buffer, SegBuf, klass) { - INHERIT_CLASS(class, BufferClass); - class->name = "SEGBUF"; - class->size = sizeof(SegBufStruct); - class->init = segBufInit; - class->finish = segBufFinish; - class->attach = segBufAttach; - class->detach = segBufDetach; - class->describe = segBufDescribe; - class->seg = segBufSeg; - class->rankSet = segBufRankSet; - class->setRankSet = segBufSetRankSet; - class->reassignSeg = segBufReassignSeg; - AVERT(BufferClass, class); + INHERIT_CLASS(klass, SegBuf, Buffer); + klass->instClassStruct.finish = segBufFinish; + klass->instClassStruct.describe = segBufDescribe; + klass->size = sizeof(SegBufStruct); + klass->init = segBufInit; + klass->attach = segBufAttach; + klass->detach = segBufDetach; + klass->seg = segBufSeg; + klass->rankSet = segBufRankSet; + klass->setRankSet = segBufSetRankSet; + klass->reassignSeg = segBufReassignSeg; + AVERT(BufferClass, klass); } @@ -1491,30 +1277,29 @@ static void rankBufVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) /* rankBufInit -- RankBufClass init method */ -static Res rankBufInit(Buffer buffer, Pool pool, ArgList args) +static Res rankBufInit(Buffer buffer, Pool pool, Bool isMutator, ArgList args) { Rank rank = BUFFER_RANK_DEFAULT; - BufferClass super; Res res; ArgStruct arg; - AVERT(Buffer, buffer); - AVERT(Pool, pool); AVERT(ArgList, args); if (ArgPick(&arg, args, MPS_KEY_RANK)) rank = arg.val.rank; AVERT(Rank, rank); /* Initialize the superclass fields first via next-method call */ - super = BUFFER_SUPERCLASS(RankBufClass); - res = super->init(buffer, pool, args); + res = NextMethod(Buffer, RankBuf, init)(buffer, pool, isMutator, args); if (res != ResOK) return res; BufferSetRankSet(buffer, RankSetSingle(rank)); - /* There's nothing to check that the superclass doesn't, so no AVERT. */ + SetClassOfPoly(buffer, CLASS(RankBuf)); + AVERC(RankBuf, buffer); + EVENT4(BufferInitRank, buffer, pool, BOOLOF(buffer->isMutator), rank); + return ResOK; } @@ -1525,21 +1310,18 @@ static Res rankBufInit(Buffer buffer, Pool pool, ArgList args) * * Supports initialization to a rank supplied at creation time. */ -typedef BufferClassStruct RankBufClassStruct; - -DEFINE_CLASS(RankBufClass, class) +DEFINE_CLASS(Buffer, RankBuf, klass) { - INHERIT_CLASS(class, SegBufClass); - class->name = "RANKBUF"; - class->varargs = rankBufVarargs; - class->init = rankBufInit; - AVERT(BufferClass, class); + INHERIT_CLASS(klass, RankBuf, SegBuf); + klass->varargs = rankBufVarargs; + klass->init = rankBufInit; + AVERT(BufferClass, klass); } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/cbs.c b/mps/code/cbs.c index 0b30b68acb1..05d169406a0 100644 --- a/mps/code/cbs.c +++ b/mps/code/cbs.c @@ -1,18 +1,25 @@ /* cbs.c: COALESCING BLOCK STRUCTURE IMPLEMENTATION * * $Id$ - * Copyright (c) 2001-2015 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * * .intro: This is a portable implementation of coalescing block * structures. * - * .purpose: CBSs are used to manage potentially unbounded - * collections of memory blocks. + * .purpose: CBSs are used to manage potentially unbounded collections + * of memory blocks. * * .sources: . + * + * .critical: In manual-allocation-bound programs using MVFF, many of + * these functions are on the critical paths via mps_alloc (and then + * PoolAlloc, MVFFAlloc, failoverFind*, cbsFind*) and mps_free (and + * then MVFFFree, failoverInsert, cbsInsert). */ #include "cbs.h" +#include "rangetree.h" +#include "range.h" #include "splay.h" #include "meter.h" #include "poolmfs.h" @@ -21,30 +28,16 @@ SRCID(cbs, "$Id$"); -#define CBSBlockBase(block) ((block)->base) -#define CBSBlockLimit(block) ((block)->limit) -#define CBSBlockSize(block) AddrOffset((block)->base, (block)->limit) - - -#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 cbsOfSplay(splay) PARENT(CBSStruct, splayTreeStruct, splay) +#define cbsFastBlockOfTree(tree) \ + PARENT(CBSFastBlockStruct, rangeTreeStruct, RangeTreeOfTree(tree)) +#define cbsFastBlockNode(block) (&(block)->rangeTreeStruct) +#define cbsZonedBlockOfTree(tree) \ + PARENT(CBSZonedBlockStruct, cbsFastBlockStruct, cbsFastBlockOfTree(tree)) +#define cbsZonedBlockNode(block) cbsFastBlockNode(&(block)->cbsFastBlockStruct) #define cbsBlockPool(cbs) RVALUE((cbs)->blockPool) -/* 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 */ @@ -60,93 +53,45 @@ Bool CBSCheck(CBS cbs) CHECKL(cbs->blockStructSize > 0); CHECKL(BoolCheck(cbs->ownPool)); CHECKL(SizeIsAligned(cbs->size, LandAlignment(land))); - STATISTIC_STAT({CHECKL((cbs->size == 0) == (cbs->treeSize == 0));}); + STATISTIC(CHECKL((cbs->size == 0) == (cbs->treeSize == 0))); return TRUE; } -ATTRIBUTE_UNUSED -static Bool CBSBlockCheck(CBSBlock block) -{ - UNUSED(block); /* Required because there is no signature */ - CHECKL(block != NULL); - /* 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. */ - CHECKL(CBSBlockBase(block) <= CBSBlockLimit(block)); - /* Can't check maxSize because it may be invalid at the time */ - return TRUE; -} - - -/* cbsCompare -- Compare key to [base,limit) - * - * See - */ - -static Compare cbsCompare(Tree tree, TreeKey key) -{ - Addr base1, base2, limit2; - CBSBlock cbsBlock; - - AVERT_CRITICAL(Tree, tree); - AVER_CRITICAL(tree != TreeEMPTY); - AVER_CRITICAL(key != NULL); - - base1 = baseOfKey(key); - cbsBlock = cbsBlockOfTree(tree); - base2 = cbsBlock->base; - limit2 = cbsBlock->limit; - - if (base1 < base2) - return CompareLESS; - else if (base1 >= limit2) - return CompareGREATER; - else - 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 splay, Tree tree, - void *closureP, Size size) +static Bool cbsTestNode(SplayTree splay, Tree tree, void *closure) { - CBSBlock block; + RangeTree block; + Size *sizeP = closure; - AVERT(SplayTree, splay); - AVERT(Tree, tree); - AVER(closureP == NULL); - AVER(size > 0); - AVER(IsLandSubclass(CBSLand(cbsOfSplay(splay)), CBSFastLandClass)); + AVERT_CRITICAL(SplayTree, splay); + AVERT_CRITICAL(Tree, tree); + AVER_CRITICAL(sizeP != NULL); + AVER_CRITICAL(*sizeP > 0); + AVER_CRITICAL(IsA(CBSFast, cbsOfSplay(splay))); - block = cbsBlockOfTree(tree); + block = RangeTreeOfTree(tree); - return CBSBlockSize(block) >= size; + return RangeTreeSize(block) >= *sizeP; } static Bool cbsTestTree(SplayTree splay, Tree tree, - void *closureP, Size size) + void *closure) { CBSFastBlock block; + Size *sizeP = closure; - AVERT(SplayTree, splay); - AVERT(Tree, tree); - AVER(closureP == NULL); - AVER(size > 0); - AVER(IsLandSubclass(CBSLand(cbsOfSplay(splay)), CBSFastLandClass)); + AVERT_CRITICAL(SplayTree, splay); + AVERT_CRITICAL(Tree, tree); + AVER_CRITICAL(sizeP != NULL); + AVER_CRITICAL(*sizeP > 0); + AVER_CRITICAL(IsA(CBSFast, cbsOfSplay(splay))); block = cbsFastBlockOfTree(tree); - return block->maxSize >= size; + return block->maxSize >= *sizeP; } @@ -158,9 +103,9 @@ static void cbsUpdateFastNode(SplayTree splay, Tree tree) AVERT_CRITICAL(SplayTree, splay); AVERT_CRITICAL(Tree, tree); - AVER_CRITICAL(IsLandSubclass(CBSLand(cbsOfSplay(splay)), CBSFastLandClass)); + AVER_CRITICAL(IsA(CBSFast, cbsOfSplay(splay))); - maxSize = CBSBlockSize(cbsBlockOfTree(tree)); + maxSize = RangeTreeSize(RangeTreeOfTree(tree)); if (TreeHasLeft(tree)) { Size size = cbsFastBlockOfTree(TreeLeft(tree))->maxSize; @@ -184,19 +129,19 @@ static void cbsUpdateZonedNode(SplayTree splay, Tree tree) { ZoneSet zones; CBSZonedBlock zonedBlock; - CBSBlock block; + RangeTree block; Arena arena; AVERT_CRITICAL(SplayTree, splay); AVERT_CRITICAL(Tree, tree); - AVER_CRITICAL(IsLandSubclass(CBSLand(cbsOfSplay(splay)), CBSZonedLandClass)); + AVER_CRITICAL(IsA(CBSZoned, cbsOfSplay(splay))); cbsUpdateFastNode(splay, tree); zonedBlock = cbsZonedBlockOfTree(tree); - block = &zonedBlock->cbsFastBlockStruct.cbsBlockStruct; + block = cbsZonedBlockNode(zonedBlock); arena = LandArena(CBSLand(cbsOfSplay(splay))); - zones = ZoneSetOfRange(arena, CBSBlockBase(block), CBSBlockLimit(block)); + zones = ZoneSetOfRange(arena, RangeTreeBase(block), RangeTreeLimit(block)); if (TreeHasLeft(tree)) zones = ZoneSetUnion(zones, cbsZonedBlockOfTree(TreeLeft(tree))->zones); @@ -215,26 +160,26 @@ static void cbsUpdateZonedNode(SplayTree splay, Tree tree) ARG_DEFINE_KEY(cbs_block_pool, Pool); -static Res cbsInitComm(Land land, ArgList args, SplayUpdateNodeFunction update, +static Res cbsInitComm(Land land, LandClass klass, + Arena arena, Align alignment, + ArgList args, SplayUpdateNodeFunction update, Size blockStructSize) { CBS cbs; - LandClass super; ArgStruct arg; Res res; Pool blockPool = NULL; - AVERT(Land, land); - super = LAND_SUPERCLASS(CBSLandClass); - res = (*super->init)(land, args); + AVER(land != NULL); + res = NextMethod(Land, CBS, init)(land, arena, alignment, args); if (res != ResOK) return res; + cbs = CouldBeA(CBS, land); if (ArgPick(&arg, args, CBSBlockPool)) blockPool = arg.val.pool; - cbs = cbsOfLand(land); - SplayTreeInit(cbsSplay(cbs), cbsCompare, cbsKey, update); + SplayTreeInit(cbsSplay(cbs), RangeTreeCompare, RangeTreeKey, update); if (blockPool != NULL) { cbs->blockPool = blockPool; @@ -248,34 +193,38 @@ static Res cbsInitComm(Land land, ArgList args, SplayUpdateNodeFunction update, return res; cbs->ownPool = TRUE; } - cbs->treeSize = 0; + STATISTIC(cbs->treeSize = 0); cbs->size = 0; cbs->blockStructSize = blockStructSize; METER_INIT(cbs->treeSearch, "size of tree", (void *)cbs); + SetClassOfPoly(land, klass); cbs->sig = CBSSig; + AVERC(CBS, cbs); - AVERT(CBS, cbs); return ResOK; } -static Res cbsInit(Land land, ArgList args) +static Res cbsInit(Land land, Arena arena, Align alignment, ArgList args) { - return cbsInitComm(land, args, SplayTrivUpdate, - sizeof(CBSBlockStruct)); + return cbsInitComm(land, CLASS(CBS), arena, alignment, + args, SplayTrivUpdate, + sizeof(RangeTreeStruct)); } -static Res cbsInitFast(Land land, ArgList args) +static Res cbsInitFast(Land land, Arena arena, Align alignment, ArgList args) { - return cbsInitComm(land, args, cbsUpdateFastNode, + return cbsInitComm(land, CLASS(CBSFast), arena, alignment, + args, cbsUpdateFastNode, sizeof(CBSFastBlockStruct)); } -static Res cbsInitZoned(Land land, ArgList args) +static Res cbsInitZoned(Land land, Arena arena, Align alignment, ArgList args) { - return cbsInitComm(land, args, cbsUpdateZonedNode, + return cbsInitComm(land, CLASS(CBSZoned), arena, alignment, + args, cbsUpdateZonedNode, sizeof(CBSZonedBlockStruct)); } @@ -285,13 +234,10 @@ static Res cbsInitZoned(Land land, ArgList args) * See . */ -static void cbsFinish(Land land) +static void cbsFinish(Inst inst) { - CBS cbs; - - AVERT(Land, land); - cbs = cbsOfLand(land); - AVERT(CBS, cbs); + Land land = MustBeA(Land, inst); + CBS cbs = MustBeA(CBS, land); METER_EMIT(&cbs->treeSearch); @@ -300,6 +246,8 @@ static void cbsFinish(Land land) SplayTreeFinish(cbsSplay(cbs)); if (cbs->ownPool) PoolDestroy(cbsBlockPool(cbs)); + + NextMethod(Inst, CBS, finish)(inst); } @@ -310,110 +258,101 @@ static void cbsFinish(Land land) static Size cbsSize(Land land) { - CBS cbs; - - AVERT(Land, land); - cbs = cbsOfLand(land); - AVERT(CBS, cbs); - + CBS cbs = MustBeA_CRITICAL(CBS, land); return cbs->size; } /* cbsBlockDestroy -- destroy a block */ -static void cbsBlockDestroy(CBS cbs, CBSBlock block) +static void cbsBlockDestroy(CBS cbs, RangeTree block) { Size size; AVERT(CBS, cbs); - AVERT(CBSBlock, block); - size = CBSBlockSize(block); + AVERT(RangeTree, block); + size = RangeTreeSize(block); STATISTIC(--cbs->treeSize); AVER(cbs->size >= size); cbs->size -= size; - /* make invalid */ - block->limit = block->base; + RangeTreeFinish(block); PoolFree(cbsBlockPool(cbs), (Addr)block, cbs->blockStructSize); } -/* Node change operators +/* RangeTree 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) +static void cbsBlockDelete(CBS cbs, RangeTree block) { Bool b; AVERT(CBS, cbs); - AVERT(CBSBlock, block); + AVERT(RangeTree, block); METER_ACC(cbs->treeSearch, cbs->treeSize); - b = SplayTreeDelete(cbsSplay(cbs), cbsBlockTree(block)); + b = SplayTreeDelete(cbsSplay(cbs), RangeTreeTree(block)); AVER(b); /* expect block to be in the tree */ cbsBlockDestroy(cbs, block); } -static void cbsBlockShrunk(CBS cbs, CBSBlock block, Size oldSize) +static void cbsBlockShrunk(CBS cbs, RangeTree block, Size oldSize) { Size newSize; AVERT(CBS, cbs); - AVERT(CBSBlock, block); + AVERT(RangeTree, block); - newSize = CBSBlockSize(block); + newSize = RangeTreeSize(block); AVER(oldSize > newSize); AVER(cbs->size >= oldSize - newSize); - SplayNodeRefresh(cbsSplay(cbs), cbsBlockTree(block)); + SplayNodeRefresh(cbsSplay(cbs), RangeTreeTree(block)); cbs->size -= oldSize - newSize; } -static void cbsBlockGrew(CBS cbs, CBSBlock block, Size oldSize) +static void cbsBlockGrew(CBS cbs, RangeTree block, Size oldSize) { Size newSize; AVERT(CBS, cbs); - AVERT(CBSBlock, block); + AVERT(RangeTree, block); - newSize = CBSBlockSize(block); + newSize = RangeTreeSize(block); AVER(oldSize < newSize); - SplayNodeRefresh(cbsSplay(cbs), cbsBlockTree(block)); + SplayNodeRefresh(cbsSplay(cbs), RangeTreeTree(block)); cbs->size += newSize - oldSize; } /* cbsBlockAlloc -- allocate a new block and set its base and limit, but do not insert it into the tree yet */ -static Res cbsBlockAlloc(CBSBlock *blockReturn, CBS cbs, Range range) +static Res cbsBlockAlloc(RangeTree *blockReturn, CBS cbs, Range range) { Res res; - CBSBlock block; + RangeTree block; Addr p; AVER(blockReturn != NULL); AVERT(CBS, cbs); AVERT(Range, range); - res = PoolAlloc(&p, cbsBlockPool(cbs), cbs->blockStructSize, - /* withReservoirPermit */ FALSE); + res = PoolAlloc(&p, cbsBlockPool(cbs), cbs->blockStructSize); if (res != ResOK) goto failPoolAlloc; - block = (CBSBlock)p; + block = (RangeTree)p; - TreeInit(cbsBlockTree(block)); - block->base = RangeBase(range); - block->limit = RangeLimit(range); + RangeTreeInitFromRange(block, range); - SplayNodeInit(cbsSplay(cbs), cbsBlockTree(block)); + SplayNodeInit(cbsSplay(cbs), RangeTreeTree(block)); - AVERT(CBSBlock, block); + AVERT(RangeTree, block); *blockReturn = block; return ResOK; @@ -424,18 +363,18 @@ static Res cbsBlockAlloc(CBSBlock *blockReturn, CBS cbs, Range range) /* cbsBlockInsert -- insert a block into the tree */ -static void cbsBlockInsert(CBS cbs, CBSBlock block) +static void cbsBlockInsert(CBS cbs, RangeTree block) { Bool b; - AVERT(CBS, cbs); - AVERT(CBSBlock, block); + AVERT_CRITICAL(CBS, cbs); + AVERT_CRITICAL(RangeTree, block); METER_ACC(cbs->treeSearch, cbs->treeSize); - b = SplayTreeInsert(cbsSplay(cbs), cbsBlockTree(block)); - AVER(b); + b = SplayTreeInsert(cbsSplay(cbs), RangeTreeTree(block)); + AVER_CRITICAL(b); STATISTIC(++cbs->treeSize); - cbs->size += CBSBlockSize(block); + cbs->size += RangeTreeSize(block); } @@ -449,97 +388,98 @@ static void cbsBlockInsert(CBS cbs, CBSBlock block) static Res cbsInsert(Range rangeReturn, Land land, Range range) { - CBS cbs; + CBS cbs = MustBeA_CRITICAL(CBS, land); Bool b; Res res; Addr base, limit, newBase, newLimit; Tree leftSplay, rightSplay; - CBSBlock leftCBS, rightCBS; + RangeTree leftBlock, rightBlock; Bool leftMerge, rightMerge; Size oldSize; - AVER(rangeReturn != NULL); - AVERT(Land, land); - AVERT(Range, range); - AVER(RangeIsAligned(range, LandAlignment(land))); + AVER_CRITICAL(rangeReturn != NULL); + AVERT_CRITICAL(Range, range); + AVER_CRITICAL(RangeIsAligned(range, LandAlignment(land))); - cbs = cbsOfLand(land); base = RangeBase(range); limit = RangeLimit(range); METER_ACC(cbs->treeSearch, cbs->treeSize); - b = SplayTreeNeighbours(&leftSplay, &rightSplay, cbsSplay(cbs), keyOfBaseVar(base)); + b = SplayTreeNeighbours(&leftSplay, &rightSplay, cbsSplay(cbs), + RangeTreeKeyOfBaseVar(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 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. - */ + /* .insert.overlap: 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 + leftBlock's limit <= base (this is ensured by RangeTreeCompare, + which is the comparison method on the tree). But if there is a + right neighbour, all we know is that base < rightBlock's base. But + for the range to fit, we need limit <= rightBlock's base too. Hence + the extra check and the possibility of failure in the second + case. */ + if (leftSplay == TreeEMPTY) { - leftCBS = NULL; + leftBlock = NULL; leftMerge = FALSE; } else { - leftCBS = cbsBlockOfTree(leftSplay); - AVER(leftCBS->limit <= base); - leftMerge = leftCBS->limit == base; + leftBlock = RangeTreeOfTree(leftSplay); + AVER_CRITICAL(RangeTreeLimit(leftBlock) <= base); + leftMerge = RangeTreeLimit(leftBlock) == base; } if (rightSplay == TreeEMPTY) { - rightCBS = NULL; + rightBlock = NULL; rightMerge = FALSE; } else { - rightCBS = cbsBlockOfTree(rightSplay); - if (rightCBS != NULL && limit > CBSBlockLimit(rightCBS)) { + rightBlock = RangeTreeOfTree(rightSplay); + if (rightBlock != NULL && limit > RangeTreeBase(rightBlock)) { + /* .insert.overlap */ res = ResFAIL; goto fail; } - rightMerge = rightCBS->base == limit; + rightMerge = RangeTreeBase(rightBlock) == limit; } - newBase = leftMerge ? CBSBlockBase(leftCBS) : base; - newLimit = rightMerge ? CBSBlockLimit(rightCBS) : limit; + newBase = leftMerge ? RangeTreeBase(leftBlock) : base; + newLimit = rightMerge ? RangeTreeLimit(rightBlock) : limit; if (leftMerge && rightMerge) { - Size oldLeftSize = CBSBlockSize(leftCBS); - Addr rightLimit = CBSBlockLimit(rightCBS); - cbsBlockDelete(cbs, rightCBS); - leftCBS->limit = rightLimit; - cbsBlockGrew(cbs, leftCBS, oldLeftSize); + Size oldLeftSize = RangeTreeSize(leftBlock); + Addr rightLimit = RangeTreeLimit(rightBlock); + cbsBlockDelete(cbs, rightBlock); + RangeTreeSetLimit(leftBlock, rightLimit); + cbsBlockGrew(cbs, leftBlock, oldLeftSize); } else if (leftMerge) { - oldSize = CBSBlockSize(leftCBS); - leftCBS->limit = limit; - cbsBlockGrew(cbs, leftCBS, oldSize); + oldSize = RangeTreeSize(leftBlock); + RangeTreeSetLimit(leftBlock, limit); + cbsBlockGrew(cbs, leftBlock, oldSize); } else if (rightMerge) { - oldSize = CBSBlockSize(rightCBS); - rightCBS->base = base; - cbsBlockGrew(cbs, rightCBS, oldSize); + oldSize = RangeTreeSize(rightBlock); + RangeTreeSetBase(rightBlock, base); + cbsBlockGrew(cbs, rightBlock, oldSize); } else { - CBSBlock block; + RangeTree block; res = cbsBlockAlloc(&block, cbs, range); if (res != ResOK) goto fail; cbsBlockInsert(cbs, block); } - AVER(newBase <= base); - AVER(newLimit >= limit); + AVER_CRITICAL(newBase <= base); + AVER_CRITICAL(newLimit >= limit); RangeInit(rangeReturn, newBase, newLimit); return ResOK; fail: - AVER(res != ResOK); + AVER_CRITICAL(res != ResOK); return res; } @@ -554,15 +494,13 @@ static Res cbsInsert(Range rangeReturn, Land land, Range range) static Res cbsDelete(Range rangeReturn, Land land, Range range) { - CBS cbs; + CBS cbs = MustBeA(CBS, land); Res res; - CBSBlock cbsBlock; + RangeTree block; Tree tree; Addr base, limit, oldBase, oldLimit; Size oldSize; - AVERT(Land, land); - cbs = cbsOfLand(land); AVER(rangeReturn != NULL); AVERT(Range, range); AVER(RangeIsAligned(range, LandAlignment(land))); @@ -571,43 +509,43 @@ static Res cbsDelete(Range rangeReturn, Land land, Range range) limit = RangeLimit(range); METER_ACC(cbs->treeSearch, cbs->treeSize); - if (!SplayTreeFind(&tree, cbsSplay(cbs), keyOfBaseVar(base))) { + if (!SplayTreeFind(&tree, cbsSplay(cbs), RangeTreeKeyOfBaseVar(base))) { res = ResFAIL; goto failSplayTreeSearch; } - cbsBlock = cbsBlockOfTree(tree); + block = RangeTreeOfTree(tree); - if (limit > cbsBlock->limit) { + if (limit > RangeTreeLimit(block)) { res = ResFAIL; goto failLimitCheck; } - oldBase = cbsBlock->base; - oldLimit = cbsBlock->limit; - oldSize = CBSBlockSize(cbsBlock); + oldBase = RangeTreeBase(block); + oldLimit = RangeTreeLimit(block); + oldSize = RangeTreeSize(block); RangeInit(rangeReturn, oldBase, oldLimit); if (base == oldBase && limit == oldLimit) { /* entire block */ - cbsBlockDelete(cbs, cbsBlock); + cbsBlockDelete(cbs, block); } else if (base == oldBase) { /* remaining fragment at right */ AVER(limit < oldLimit); - cbsBlock->base = limit; - cbsBlockShrunk(cbs, cbsBlock, oldSize); + RangeTreeSetBase(block, limit); + cbsBlockShrunk(cbs, block, oldSize); } else if (limit == oldLimit) { /* remaining fragment at left */ AVER(base > oldBase); - cbsBlock->limit = base; - cbsBlockShrunk(cbs, cbsBlock, oldSize); + RangeTreeSetLimit(block, base); + cbsBlockShrunk(cbs, block, oldSize); } else { /* two remaining fragments. shrink block to represent fragment at left, and create new block for fragment at right. */ RangeStruct newRange; - CBSBlock newBlock; + RangeTree newBlock; AVER(base > oldBase); AVER(limit < oldLimit); RangeInit(&newRange, limit, oldLimit); @@ -615,8 +553,8 @@ static Res cbsDelete(Range rangeReturn, Land land, Range range) if (res != ResOK) { goto failAlloc; } - cbsBlock->limit = base; - cbsBlockShrunk(cbs, cbsBlock, oldSize); + RangeTreeSetLimit(block, base); + cbsBlockShrunk(cbs, block, oldSize); cbsBlockInsert(cbs, newBlock); } @@ -630,7 +568,7 @@ static Res cbsDelete(Range rangeReturn, Land land, Range range) } -static Res cbsBlockDescribe(CBSBlock block, mps_lib_FILE *stream) +static Res cbsBlockDescribe(RangeTree block, mps_lib_FILE *stream) { Res res; @@ -639,8 +577,8 @@ static Res cbsBlockDescribe(CBSBlock block, mps_lib_FILE *stream) res = WriteF(stream, 0, "[$P,$P)", - (WriteFP)block->base, - (WriteFP)block->limit, + (WriteFP)RangeTreeBase(block), + (WriteFP)RangeTreeLimit(block), NULL); return res; } @@ -654,7 +592,7 @@ static Res cbsSplayNodeDescribe(Tree tree, mps_lib_FILE *stream) if (stream == NULL) return ResFAIL; - res = cbsBlockDescribe(cbsBlockOfTree(tree), stream); + res = cbsBlockDescribe(RangeTreeOfTree(tree), stream); return res; } @@ -667,8 +605,8 @@ static Res cbsFastBlockDescribe(CBSFastBlock block, mps_lib_FILE *stream) res = WriteF(stream, 0, "[$P,$P) {$U}", - (WriteFP)block->cbsBlockStruct.base, - (WriteFP)block->cbsBlockStruct.limit, + (WriteFP)RangeTreeBase(cbsFastBlockNode(block)), + (WriteFP)RangeTreeLimit(cbsFastBlockNode(block)), (WriteFU)block->maxSize, NULL); return res; @@ -696,8 +634,8 @@ static Res cbsZonedBlockDescribe(CBSZonedBlock block, mps_lib_FILE *stream) res = WriteF(stream, 0, "[$P,$P) {$U, $B}", - (WriteFP)block->cbsFastBlockStruct.cbsBlockStruct.base, - (WriteFP)block->cbsFastBlockStruct.cbsBlockStruct.limit, + (WriteFP)RangeTreeBase(cbsZonedBlockNode(block)), + (WriteFP)RangeTreeLimit(cbsZonedBlockNode(block)), (WriteFU)block->cbsFastBlockStruct.maxSize, (WriteFB)block->zones, NULL); @@ -726,29 +664,25 @@ static Res cbsZonedSplayNodeDescribe(Tree tree, mps_lib_FILE *stream) typedef struct CBSIterateClosure { Land land; LandVisitor visitor; - void *closureP; + void *visitorClosure; } CBSIterateClosure; -static Bool cbsIterateVisit(Tree tree, void *closureP, Size closureS) +static Bool cbsIterateVisit(Tree tree, void *closure) { - CBSIterateClosure *closure = closureP; - Land land = closure->land; - CBSBlock cbsBlock = cbsBlockOfTree(tree); + CBSIterateClosure *my = closure; + Land land = my->land; + RangeTree block = RangeTreeOfTree(tree); RangeStruct range; - RangeInit(&range, CBSBlockBase(cbsBlock), CBSBlockLimit(cbsBlock)); - return (*closure->visitor)(land, &range, closure->closureP, closureS); + RangeInit(&range, RangeTreeBase(block), RangeTreeLimit(block)); + return my->visitor(land, &range, my->visitorClosure); } -static Bool cbsIterate(Land land, LandVisitor visitor, - void *closureP, Size closureS) +static Bool cbsIterate(Land land, LandVisitor visitor, void *visitorClosure) { - CBS cbs; + CBS cbs = MustBeA(CBS, land); SplayTree splay; - CBSIterateClosure closure; + CBSIterateClosure iterateClosure; - AVERT(Land, land); - cbs = cbsOfLand(land); - AVERT(CBS, cbs); AVER(FUNCHECK(visitor)); splay = cbsSplay(cbs); @@ -756,11 +690,11 @@ static Bool cbsIterate(Land land, LandVisitor visitor, /* searches and meter it. */ METER_ACC(cbs->treeSearch, cbs->treeSize); - closure.land = land; - closure.visitor = visitor; - closure.closureP = closureP; + iterateClosure.land = land; + iterateClosure.visitor = visitor; + iterateClosure.visitorClosure = visitorClosure; return TreeTraverse(SplayTreeRoot(splay), splay->compare, splay->nodeKey, - cbsIterateVisit, &closure, closureS); + cbsIterateVisit, &iterateClosure); } @@ -773,37 +707,34 @@ typedef struct CBSIterateAndDeleteClosure { Land land; LandDeleteVisitor visitor; Bool cont; - void *closureP; + void *visitorClosure; } CBSIterateAndDeleteClosure; -static Bool cbsIterateAndDeleteVisit(Tree tree, void *closureP, Size closureS) +static Bool cbsIterateAndDeleteVisit(Tree tree, void *closure) { - CBSIterateAndDeleteClosure *closure = closureP; - Land land = closure->land; - CBS cbs = cbsOfLand(land); - CBSBlock cbsBlock = cbsBlockOfTree(tree); + CBSIterateAndDeleteClosure *my = closure; + Land land = my->land; + CBS cbs = MustBeA(CBS, land); + RangeTree block = RangeTreeOfTree(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); + RangeInit(&range, RangeTreeBase(block), RangeTreeLimit(block)); + if (my->cont) + my->cont = my->visitor(&deleteNode, land, &range, + my->visitorClosure); if (deleteNode) - cbsBlockDestroy(cbs, cbsBlock); + cbsBlockDestroy(cbs, block); return deleteNode; } static Bool cbsIterateAndDelete(Land land, LandDeleteVisitor visitor, - void *closureP, Size closureS) + void *visitorClosure) { - CBS cbs; + CBS cbs = MustBeA(CBS, land); SplayTree splay; - CBSIterateAndDeleteClosure closure; + CBSIterateAndDeleteClosure iterateClosure; - AVERT(Land, land); - cbs = cbsOfLand(land); - AVERT(CBS, cbs); AVER(FUNCHECK(visitor)); splay = cbsSplay(cbs); @@ -811,13 +742,13 @@ static Bool cbsIterateAndDelete(Land land, LandDeleteVisitor visitor, /* searches and meter it. */ METER_ACC(cbs->treeSearch, cbs->treeSize); - closure.land = land; - closure.visitor = visitor; - closure.closureP = closureP; - closure.cont = TRUE; + iterateClosure.land = land; + iterateClosure.visitor = visitor; + iterateClosure.visitorClosure = visitorClosure; + iterateClosure.cont = TRUE; TreeTraverseAndDelete(&splay->root, cbsIterateAndDeleteVisit, - &closure, closureS); - return closure.cont; + &iterateClosure); + return iterateClosure.cont; } @@ -887,31 +818,26 @@ static void cbsFindDeleteRange(Range rangeReturn, Range oldRangeReturn, static Bool cbsFindFirst(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) { - CBS cbs; + CBS cbs = MustBeA_CRITICAL(CBS, land); Bool found; Tree tree; - AVERT(Land, land); - cbs = cbsOfLand(land); - AVERT(CBS, cbs); - AVER(IsLandSubclass(CBSLand(cbs), CBSFastLandClass)); - - AVER(rangeReturn != NULL); - AVER(oldRangeReturn != NULL); - AVER(size > 0); - AVER(SizeIsAligned(size, LandAlignment(land))); - AVERT(FindDelete, findDelete); + AVER_CRITICAL(rangeReturn != NULL); + AVER_CRITICAL(oldRangeReturn != NULL); + AVER_CRITICAL(size > 0); + AVER_CRITICAL(SizeIsAligned(size, LandAlignment(land))); + AVERT_CRITICAL(FindDelete, findDelete); METER_ACC(cbs->treeSearch, cbs->treeSize); found = SplayFindFirst(&tree, cbsSplay(cbs), &cbsTestNode, - &cbsTestTree, NULL, size); + &cbsTestTree, &size); if (found) { - CBSBlock block; + RangeTree block; RangeStruct range; - block = cbsBlockOfTree(tree); - AVER(CBSBlockSize(block) >= size); - RangeInit(&range, CBSBlockBase(block), CBSBlockLimit(block)); - AVER(RangeSize(&range) >= size); + block = RangeTreeOfTree(tree); + AVER_CRITICAL(RangeTreeSize(block) >= size); + RangeInit(&range, RangeTreeBase(block), RangeTreeLimit(block)); + AVER_CRITICAL(RangeSize(&range) >= size); cbsFindDeleteRange(rangeReturn, oldRangeReturn, land, &range, size, findDelete); } @@ -919,9 +845,12 @@ static Bool cbsFindFirst(Range rangeReturn, Range oldRangeReturn, 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.) + +/* cbsFindInZones -- find a block within a zone set + * + * Finds 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 { @@ -934,36 +863,34 @@ typedef struct cbsTestNodeInZonesClosureStruct { } cbsTestNodeInZonesClosureStruct, *cbsTestNodeInZonesClosure; static Bool cbsTestNodeInZones(SplayTree splay, Tree tree, - void *closureP, Size closureS) + void *closure) { - CBSBlock block = cbsBlockOfTree(tree); - cbsTestNodeInZonesClosure closure = closureP; + RangeTree block = RangeTreeOfTree(tree); + cbsTestNodeInZonesClosure my = closure; RangeInZoneSet search; - + + AVER_CRITICAL(closure != NULL); UNUSED(splay); - AVER(closureS == UNUSED_SIZE); - UNUSED(closureS); - search = closure->high ? RangeInZoneSetLast : RangeInZoneSetFirst; + search = my->high ? RangeInZoneSetLast : RangeInZoneSetFirst; - return search(&closure->base, &closure->limit, - CBSBlockBase(block), CBSBlockLimit(block), - closure->arena, closure->zoneSet, closure->size); + return search(&my->base, &my->limit, + RangeTreeBase(block), RangeTreeLimit(block), + my->arena, my->zoneSet, my->size); } static Bool cbsTestTreeInZones(SplayTree splay, Tree tree, - void *closureP, Size closureS) + void *closure) { CBSFastBlock fastBlock = cbsFastBlockOfTree(tree); CBSZonedBlock zonedBlock = cbsZonedBlockOfTree(tree); - cbsTestNodeInZonesClosure closure = closureP; + cbsTestNodeInZonesClosure my = closure; + AVER_CRITICAL(closure != NULL); UNUSED(splay); - AVER(closureS == UNUSED_SIZE); - UNUSED(closureS); - return fastBlock->maxSize >= closure->size - && ZoneSetInter(zonedBlock->zones, closure->zoneSet) != ZoneSetEMPTY; + return fastBlock->maxSize >= my->size + && ZoneSetInter(zonedBlock->zones, my->zoneSet) != ZoneSetEMPTY; } @@ -972,31 +899,26 @@ static Bool cbsTestTreeInZones(SplayTree splay, Tree tree, static Bool cbsFindLast(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) { - CBS cbs; + CBS cbs = MustBeA_CRITICAL(CBSFast, land); Bool found; Tree tree; - AVERT(Land, land); - cbs = cbsOfLand(land); - AVERT(CBS, cbs); - AVER(IsLandSubclass(CBSLand(cbs), CBSFastLandClass)); - - AVER(rangeReturn != NULL); - AVER(oldRangeReturn != NULL); - AVER(size > 0); - AVER(SizeIsAligned(size, LandAlignment(land))); - AVERT(FindDelete, findDelete); + AVER_CRITICAL(rangeReturn != NULL); + AVER_CRITICAL(oldRangeReturn != NULL); + AVER_CRITICAL(size > 0); + AVER_CRITICAL(SizeIsAligned(size, LandAlignment(land))); + AVERT_CRITICAL(FindDelete, findDelete); METER_ACC(cbs->treeSearch, cbs->treeSize); found = SplayFindLast(&tree, cbsSplay(cbs), &cbsTestNode, - &cbsTestTree, NULL, size); + &cbsTestTree, &size); if (found) { - CBSBlock block; + RangeTree block; RangeStruct range; - block = cbsBlockOfTree(tree); - AVER(CBSBlockSize(block) >= size); - RangeInit(&range, CBSBlockBase(block), CBSBlockLimit(block)); - AVER(RangeSize(&range) >= size); + block = RangeTreeOfTree(tree); + AVER_CRITICAL(RangeTreeSize(block) >= size); + RangeInit(&range, RangeTreeBase(block), RangeTreeLimit(block)); + AVER_CRITICAL(RangeSize(&range) >= size); cbsFindDeleteRange(rangeReturn, oldRangeReturn, land, &range, size, findDelete); } @@ -1010,18 +932,13 @@ static Bool cbsFindLast(Range rangeReturn, Range oldRangeReturn, static Bool cbsFindLargest(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) { - CBS cbs; + CBS cbs = MustBeA_CRITICAL(CBSFast, land); Bool found = FALSE; - AVERT(Land, land); - cbs = cbsOfLand(land); - AVERT(CBS, cbs); - AVER(IsLandSubclass(CBSLand(cbs), CBSFastLandClass)); - - AVER(rangeReturn != NULL); - AVER(oldRangeReturn != NULL); - AVER(size > 0); - AVERT(FindDelete, findDelete); + AVER_CRITICAL(rangeReturn != NULL); + AVER_CRITICAL(oldRangeReturn != NULL); + AVER_CRITICAL(size > 0); + AVERT_CRITICAL(FindDelete, findDelete); if (!SplayTreeIsEmpty(cbsSplay(cbs))) { RangeStruct range; @@ -1030,15 +947,15 @@ static Bool cbsFindLargest(Range rangeReturn, Range oldRangeReturn, maxSize = cbsFastBlockOfTree(SplayTreeRoot(cbsSplay(cbs)))->maxSize; if (maxSize >= size) { - CBSBlock block; + RangeTree 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 = cbsBlockOfTree(tree); - AVER(CBSBlockSize(block) >= maxSize); - RangeInit(&range, CBSBlockBase(block), CBSBlockLimit(block)); - AVER(RangeSize(&range) >= maxSize); + &cbsTestTree, &maxSize); + AVER_CRITICAL(found); /* maxSize is exact, so we will find it. */ + block = RangeTreeOfTree(tree); + AVER_CRITICAL(RangeTreeSize(block) >= maxSize); + RangeInit(&range, RangeTreeBase(block), RangeTreeLimit(block)); + AVER_CRITICAL(RangeSize(&range) >= maxSize); cbsFindDeleteRange(rangeReturn, oldRangeReturn, land, &range, size, findDelete); } @@ -1052,8 +969,8 @@ static Res cbsFindInZones(Bool *foundReturn, Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high) { - CBS cbs; - CBSBlock block; + CBS cbs = MustBeA_CRITICAL(CBSZoned, land); + RangeTree block; Tree tree; cbsTestNodeInZonesClosureStruct closure; Res res; @@ -1061,15 +978,11 @@ static Res cbsFindInZones(Bool *foundReturn, Range rangeReturn, 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); + AVER_CRITICAL(foundReturn != NULL); + AVER_CRITICAL(rangeReturn != NULL); + AVER_CRITICAL(oldRangeReturn != NULL); + /* AVERT_CRITICAL(ZoneSet, zoneSet); */ + AVERT_CRITICAL(Bool, high); landFind = high ? cbsFindLast : cbsFindFirst; splayFind = high ? SplayFindLast : SplayFindFirst; @@ -1093,15 +1006,15 @@ static Res cbsFindInZones(Bool *foundReturn, Range rangeReturn, closure.high = high; if (!(*splayFind)(&tree, cbsSplay(cbs), cbsTestNodeInZones, cbsTestTreeInZones, - &closure, UNUSED_SIZE)) + &closure)) goto fail; - block = cbsBlockOfTree(tree); + block = RangeTreeOfTree(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)); + AVER_CRITICAL(RangeTreeBase(block) <= closure.base); + AVER_CRITICAL(AddrOffset(closure.base, closure.limit) >= size); + AVER_CRITICAL(ZoneSetSub(ZoneSetOfRange(LandArena(land), closure.base, closure.limit), zoneSet)); + AVER_CRITICAL(closure.limit <= RangeTreeLimit(block)); if (!high) RangeInit(&rangeStruct, closure.base, AddrAdd(closure.base, size)); @@ -1127,34 +1040,37 @@ static Res cbsFindInZones(Bool *foundReturn, Range rangeReturn, * See . */ -static Res cbsDescribe(Land land, mps_lib_FILE *stream, Count depth) +static Res cbsDescribe(Inst inst, mps_lib_FILE *stream, Count depth) { - CBS cbs; + Land land = CouldBeA(Land, inst); + CBS cbs = CouldBeA(CBS, land); Res res; Res (*describe)(Tree, mps_lib_FILE *); - if (!TESTT(Land, land)) - return ResFAIL; - cbs = cbsOfLand(land); - if (!TESTT(CBS, cbs)) - return ResFAIL; + if (!TESTC(CBS, cbs)) + return ResPARAM; if (stream == NULL) - return ResFAIL; + return ResPARAM; - res = WriteF(stream, depth, - "CBS $P {\n", (WriteFP)cbs, - " blockPool: $P\n", (WriteFP)cbsBlockPool(cbs), - " ownPool: $U\n", (WriteFU)cbs->ownPool, - " treeSize: $U\n", (WriteFU)cbs->treeSize, + res = NextMethod(Inst, CBS, describe)(inst, stream, depth); + if (res != ResOK) + return res; + + res = WriteF(stream, depth + 2, + "blockPool $P\n", (WriteFP)cbsBlockPool(cbs), + "ownPool $U\n", (WriteFU)cbs->ownPool, + STATISTIC_WRITE(" treeSize: $U\n", (WriteFU)cbs->treeSize) NULL); if (res != ResOK) return res; METER_WRITE(cbs->treeSearch, stream, depth + 2); - if (IsLandSubclass(land, CBSZonedLandClass)) + /* This could be done by specialised methods in subclasses, but it + doesn't really come out any neater. */ + if (IsA(CBSZoned, land)) describe = cbsZonedSplayNodeDescribe; - else if (IsLandSubclass(land, CBSFastLandClass)) + else if (IsA(CBSFast, land)) describe = cbsFastSplayNodeDescribe; else describe = cbsSplayNodeDescribe; @@ -1163,52 +1079,46 @@ static Res cbsDescribe(Land land, mps_lib_FILE *stream, Count depth) 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) +DEFINE_CLASS(Land, CBS, klass) { - 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); + INHERIT_CLASS(klass, CBS, Land); + klass->instClassStruct.describe = cbsDescribe; + klass->instClassStruct.finish = cbsFinish; + klass->size = sizeof(CBSStruct); + klass->init = cbsInit; + klass->sizeMethod = cbsSize; + klass->insert = cbsInsert; + klass->delete = cbsDelete; + klass->iterate = cbsIterate; + klass->iterateAndDelete = cbsIterateAndDelete; + klass->findFirst = cbsFindFirst; + klass->findLast = cbsFindLast; + klass->findLargest = cbsFindLargest; + klass->findInZones = cbsFindInZones; + AVERT(LandClass, klass); } -DEFINE_LAND_CLASS(CBSFastLandClass, class) +DEFINE_CLASS(Land, CBSFast, klass) { - INHERIT_CLASS(class, CBSLandClass); - class->name = "FASTCBS"; - class->init = cbsInitFast; - AVERT(LandClass, class); + INHERIT_CLASS(klass, CBSFast, CBS); + klass->init = cbsInitFast; + AVERT(LandClass, klass); } -DEFINE_LAND_CLASS(CBSZonedLandClass, class) +DEFINE_CLASS(Land, CBSZoned, klass) { - INHERIT_CLASS(class, CBSFastLandClass); - class->name = "ZONEDCBS"; - class->init = cbsInitZoned; - AVERT(LandClass, class); + INHERIT_CLASS(klass, CBSZoned, CBSFast); + klass->init = cbsInitZoned; + AVERT(LandClass, klass); } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2015 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/cbs.h b/mps/code/cbs.h index a1496b3f771..844cd8cba6c 100644 --- a/mps/code/cbs.h +++ b/mps/code/cbs.h @@ -11,20 +11,14 @@ #include "arg.h" #include "mpmtypes.h" +#include "mpm.h" #include "mpmst.h" -#include "range.h" +#include "rangetree.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; + struct RangeTreeStruct rangeTreeStruct; Size maxSize; /* accurate maximum block size of sub-tree */ } CBSFastBlockStruct; @@ -34,14 +28,25 @@ typedef struct CBSZonedBlockStruct { ZoneSet zones; /* union zone set of all ranges in sub-tree */ } CBSZonedBlockStruct; -typedef struct CBSStruct *CBS; +typedef struct CBSStruct *CBS, *CBSFast, *CBSZoned; extern Bool CBSCheck(CBS cbs); + + +/* CBSLand -- convert CBS to Land + * + * We would like to use MustBeA(Land, cbs) for this, but it produces + * bogus warnings about strict aliasing from GCC 4.7 (and probably + * 4.8). We can abolish this macro when those are no longer in use in + * MPS development. + */ + #define CBSLand(cbs) (&(cbs)->landStruct) -extern LandClass CBSLandClassGet(void); -extern LandClass CBSFastLandClassGet(void); -extern LandClass CBSZonedLandClassGet(void); + +DECLARE_CLASS(Land, CBS, Land); +DECLARE_CLASS(Land, CBSFast, CBS); +DECLARE_CLASS(Land, CBSZoned, CBSFast); extern const struct mps_key_s _mps_key_cbs_block_pool; #define CBSBlockPool (&_mps_key_cbs_block_pool) diff --git a/mps/code/check.h b/mps/code/check.h index a2450bebd9a..eb7219bbe05 100644 --- a/mps/code/check.h +++ b/mps/code/check.h @@ -1,7 +1,7 @@ /* check.h: ASSERTION INTERFACE * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2017 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. @@ -37,6 +37,7 @@ #include "config.h" #include "misc.h" #include "mpslib.h" +#include "protocol.h" /* ASSERT -- basic assertion @@ -51,12 +52,22 @@ #define ASSERT(cond, condstring) \ BEGIN \ - if (cond) NOOP; else \ + if (LIKELY(cond)) NOOP; else \ mps_lib_assert_fail(MPS_FILE, __LINE__, (condstring)); \ END +#define ASSERTP(cond, condstring, default_) \ + ((void)(LIKELY(cond) \ + || (mps_lib_assert_fail(MPS_FILE, __LINE__, (condstring)), FALSE)), \ + (default_)) + +#define ASSERT_ISTYPE(type, val) (type ## Check(val)) #define ASSERT_TYPECHECK(type, val) \ - ASSERT(type ## Check(val), "TypeCheck " #type ": " #val) + ASSERT(ASSERT_ISTYPE(type, val), "TypeCheck " #type ": " #val) + +#define ASSERT_ISCLASS(klass, val) (klass ## Check(CouldBeA(klass, val))) +#define ASSERT_CLASSCHECK(klass, val) \ + ASSERT(ASSERT_ISCLASS(klass, val), "ClassCheck " #klass ": " #val) #define ASSERT_NULLCHECK(type, val) \ ASSERT((val) != NULL, "NullCheck " #type ": " #val) @@ -103,28 +114,44 @@ extern unsigned CheckLevel; #endif -/* AVER, AVERT -- MPM assertions +/* AVER, AVERT, AVERC, AVERP -- MPM assertions * - * AVER and AVERT are used to assert conditions in the code. AVER checks - * an expression. AVERT checks that a value is of the correct type and - * may perform consistency checks on the value. + * AVER and friends are used to assert conditions in the code. * - * AVER and AVERT are on by default, and check conditions even in "hot" - * varieties intended to work in production. To avoid the cost of a check - * in critical parts of the code, use AVER_CRITICAL and AVERT_CRITICAL, - * but only when you've *proved* that this makes a difference to performance - * that affects requirements. + * AVER checks an expression. + * + * AVERT checks that a value is of the correct type and may perform + * consistency checks on the value by calling a check function. + * + * AVERC checks that a value is of the correct class (including + * subclasses) and may perform consistency checks on the value by + * calling a check function. + * + * AVERP checks an expression but is itself a void * expression, and + * so can be used in expression macros. + * + * AVER etc. are on by default, and check conditions even in "hot" + * varieties intended to work in production. To avoid the cost of a + * check in critical parts of the code, use AVER_CRITICAL etc., but + * only when you've *proved* that this makes a difference to + * performance that affects requirements. */ #if defined(AVER_AND_CHECK_NONE) #define AVER(cond) DISCARD(cond) -#define AVERT(type, val) DISCARD(type ## Check(val)) +#define AVERT(type, val) DISCARD(ASSERT_ISTYPE(type, val)) +#define AVERC(klass, val) DISCARD(ASSERT_ISCLASS(klass, val)) +#define AVERP(cond, dflt) (DISCARD_EXP(cond), dflt) +#define AVERPC(cond, condstring, dflt) (DISCARD_EXP(cond), dflt) #else #define AVER(cond) ASSERT(cond, #cond) #define AVERT ASSERT_TYPECHECK +#define AVERC ASSERT_CLASSCHECK +#define AVERP(cond, dflt) ASSERTP(cond, #cond, dflt) +#define AVERPC ASSERTP #endif @@ -132,11 +159,17 @@ extern unsigned CheckLevel; #define AVER_CRITICAL(cond) ASSERT(cond, #cond) #define AVERT_CRITICAL ASSERT_TYPECHECK +#define AVERC_CRITICAL ASSERT_CLASSCHECK +#define AVERP_CRITICAL(cond, dflt) ASSERTP(cond, #cond, dflt) +#define AVERPC_CRITICAL ASSERTP #else #define AVER_CRITICAL DISCARD -#define AVERT_CRITICAL(type, val) DISCARD(type ## Check(val)) +#define AVERT_CRITICAL(type, val) DISCARD(ASSERT_ISTYPE(type, val)) +#define AVERC_CRITICAL(klass, val) DISCARD(ASSERT_ISCLASS(klass, val)) +#define AVERP_CRITICAL(cond, dflt) (DISCARD_EXP(cond), dflt) +#define AVERPC_CRITICAL(cond, condstring, dflt) (DISCARD_EXP(cond), dflt) #endif @@ -170,16 +203,27 @@ extern unsigned CheckLevel; #define TESTT(type, val) ((val) != NULL && (val)->sig == type ## Sig) -/* CHECKS -- Check Signature +/* TESTC -- check class simply + * + * TODO: Does this need to be thread safe like TESTT? + */ + +#define TESTC(klass, val) ((val) != NULL && IsA(klass, val)) + + +/* CHECKS, CHECKC -- Check Signature, Check Class * * (if CHECKLEVEL == CheckLevelMINIMAL, this is all we check) */ #if defined(AVER_AND_CHECK_NONE) #define CHECKS(type, val) DISCARD(TESTT(type, val)) +#define CHECKC(klass, val) DISCARD(MustBeA(klass, val)) #else #define CHECKS(type, val) \ ASSERT(TESTT(type, val), "SigCheck " #type ": " #val) +#define CHECKC(klass, val) \ + ASSERT(TESTC(klass, val), "ClassCheck " #klass ": " #val) #endif @@ -253,6 +297,11 @@ extern unsigned CheckLevel; ASSERT_NULLCHECK(type, val), \ ASSERT_TYPECHECK(type, val)) +#define CHECKD_CLASS(klass, val) \ + CHECK_BY_LEVEL(NOOP, \ + CHECKC(klass, val) \ + ASSERT_CLASSCHECK(klass, val)) + #define CHECKU(type, val) \ CHECK_BY_LEVEL(NOOP, \ CHECKS(type, val), \ @@ -265,15 +314,16 @@ extern unsigned CheckLevel; #else /* AVER_AND_CHECK_ALL, not */ -/* TODO: This gives comparable performance to white-hot when compiling +/* TODO: This gives comparable performance to RASH when compiling using mps.c and -O2 (to get check methods inlined), but is it a bit too minimal? How much do we rely on check methods? */ -#define CHECKL(cond) DISCARD(cond) -#define CHECKD(type, val) DISCARD(TESTT(type, val)) -#define CHECKD_NOSIG(type, val) DISCARD((val) != NULL) -#define CHECKU(type, val) DISCARD(TESTT(type, val)) -#define CHECKU_NOSIG(type, val) DISCARD((val) != NULL) +#define CHECKL(cond) DISCARD(cond) +#define CHECKD(type, val) DISCARD(TESTT(type, val)) +#define CHECKD_NOSIG(type, val) DISCARD((val) != NULL) +#define CHECKD_CLASS(klass, val) DISCARD((val) != NULL) +#define CHECKU(type, val) DISCARD(TESTT(type, val)) +#define CHECKU_NOSIG(type, val) DISCARD((val) != NULL) #endif /* AVER_AND_CHECK_ALL */ @@ -324,7 +374,7 @@ extern unsigned CheckLevel; /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2017 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/comm.gmk b/mps/code/comm.gmk index e5af99266ae..d323b3ea67d 100644 --- a/mps/code/comm.gmk +++ b/mps/code/comm.gmk @@ -73,9 +73,9 @@ endif # EXTRA TARGETS # # Don't build mpseventsql by default (might not have sqlite3 installed), -# but do build mpseventcnv and mpseventtxt. +# but do build mpseventcnv, mpseventpy and mpseventtxt. -EXTRA_TARGETS ?= mpseventcnv mpseventtxt +EXTRA_TARGETS ?= mpseventcnv mpseventpy mpseventtxt # @@ -192,11 +192,10 @@ MPMCOMMON = \ poolabs.c \ poolmfs.c \ poolmrg.c \ - poolmv.c \ protocol.c \ range.c \ + rangetree.c \ ref.c \ - reserv.c \ ring.c \ root.c \ sa.c \ @@ -267,6 +266,7 @@ TEST_TARGETS=\ expt825 \ finalcv \ finaltest \ + forktest \ fotest \ gcbench \ landtest \ @@ -284,6 +284,7 @@ TEST_TARGETS=\ qs \ sacss \ segsmss \ + sncss \ steptest \ tagtest \ teletest \ @@ -319,18 +320,41 @@ $(addprefix $(PFM)/$(VARIETY)/,$(TEST_SUITES)): $(TEST_TARGETS) ../tool/testrun.sh -s "$(notdir $@)" "$(PFM)/$(VARIETY)" +# == Automated performance testing == +# +# testratio = measure performance ratio of hot variety versus rash + +TESTRATIO_SEED = 1564912146 + +define ratio +TIME_HOT=$$(/usr/bin/time -p $(PFM)/hot/$(1) -x $(TESTRATIO_SEED) $(2) 2>&1 | tail -2 | awk '{T += $$2} END {print T}'); \ +TIME_RASH=$$(/usr/bin/time -p $(PFM)/rash/$(1) -x $(TESTRATIO_SEED) $(2) 2>&1 | tail -2 | awk '{T += $$2} END {print T}'); \ +RATIO=$$(awk "BEGIN{print int(100 * $$TIME_HOT / $$TIME_RASH)}"); \ +printf "Performance ratio (hot/rash) for $(2): %d%%\n" $$RATIO +endef + +testratio: phony + $(MAKE) -f $(PFM).gmk VARIETY=hot djbench gcbench + $(MAKE) -f $(PFM).gmk VARIETY=rash djbench gcbench + $(call ratio,gcbench,amc) + $(call ratio,djbench,mvff) + + # == MMQA test suite == # # See test/README for documentation on running the MMQA test suite. -MMQA=perl test/qa -i ../code -l ../code/$(PFM)/$(VARIETY)/mps.o +MMQA=perl test/qa -p $(PFM) -v $(VARIETY) $(PFM)/$(VARIETY)/testmmqa: - $(MAKE) -f $(PFM).gmk VARIETY=$(VARIETY) TARGET=mps.o variety - (if [ "$(VARIETY)" = "cool" ]; then cd ../test && $(MMQA) runset testsets/coolonly; fi) - (cd ../test && $(MMQA) runset testsets/argerr) - (cd ../test && $(MMQA) runset testsets/conerr) - (cd ../test && $(MMQA) runset testsets/passing) + if [ "$(VARIETY)" = "cool" ]; then (cd ../test && $(MMQA) runset testsets/coolonly); fi + (cd ../test && $(MMQA) runset testsets/argerr testsets/conerr testsets/passing) + + +# == Toy Scheme interpreter == + +testscheme: phony + $(MAKE) -C ../example/scheme test # These convenience targets allow one to type "make foo" to build target @@ -447,10 +471,10 @@ $(PFM)/$(VARIETY)/arenacv: $(PFM)/$(VARIETY)/arenacv.o \ $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a $(PFM)/$(VARIETY)/awlut: $(PFM)/$(VARIETY)/awlut.o \ - $(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a + $(FMTDYTSTOBJ) $(TESTLIBOBJ) $(TESTTHROBJ) $(PFM)/$(VARIETY)/mps.a $(PFM)/$(VARIETY)/awluthe: $(PFM)/$(VARIETY)/awluthe.o \ - $(FMTHETSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a + $(FMTHETSTOBJ) $(TESTLIBOBJ) $(TESTTHROBJ) $(PFM)/$(VARIETY)/mps.a $(PFM)/$(VARIETY)/awlutth: $(PFM)/$(VARIETY)/awlutth.o \ $(FMTDYTSTOBJ) $(TESTLIBOBJ) $(TESTTHROBJ) $(PFM)/$(VARIETY)/mps.a @@ -476,6 +500,9 @@ $(PFM)/$(VARIETY)/finalcv: $(PFM)/$(VARIETY)/finalcv.o \ $(PFM)/$(VARIETY)/finaltest: $(PFM)/$(VARIETY)/finaltest.o \ $(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a +$(PFM)/$(VARIETY)/forktest: $(PFM)/$(VARIETY)/forktest.o \ + $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a + $(PFM)/$(VARIETY)/fotest: $(PFM)/$(VARIETY)/fotest.o \ $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a @@ -527,15 +554,18 @@ $(PFM)/$(VARIETY)/sacss: $(PFM)/$(VARIETY)/sacss.o \ $(PFM)/$(VARIETY)/segsmss: $(PFM)/$(VARIETY)/segsmss.o \ $(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a +$(PFM)/$(VARIETY)/sncss: $(PFM)/$(VARIETY)/sncss.o \ + $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a + +$(PFM)/$(VARIETY)/steptest: $(PFM)/$(VARIETY)/steptest.o \ + $(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a + $(PFM)/$(VARIETY)/tagtest: $(PFM)/$(VARIETY)/tagtest.o \ $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a $(PFM)/$(VARIETY)/teletest: $(PFM)/$(VARIETY)/teletest.o \ $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a -$(PFM)/$(VARIETY)/steptest: $(PFM)/$(VARIETY)/steptest.o \ - $(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a - $(PFM)/$(VARIETY)/walkt0: $(PFM)/$(VARIETY)/walkt0.o \ $(FMTDYTSTOBJ) $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a @@ -548,6 +578,9 @@ $(PFM)/$(VARIETY)/zmess: $(PFM)/$(VARIETY)/zmess.o \ $(PFM)/$(VARIETY)/mpseventcnv: $(PFM)/$(VARIETY)/eventcnv.o \ $(PFM)/$(VARIETY)/mps.a +$(PFM)/$(VARIETY)/mpseventpy: $(PFM)/$(VARIETY)/eventpy.o \ + $(PFM)/$(VARIETY)/mps.a + $(PFM)/$(VARIETY)/mpseventtxt: $(PFM)/$(VARIETY)/eventtxt.o \ $(PFM)/$(VARIETY)/mps.a diff --git a/mps/code/commpost.nmk b/mps/code/commpost.nmk index 201094905bd..55cb47aaaa0 100644 --- a/mps/code/commpost.nmk +++ b/mps/code/commpost.nmk @@ -1,7 +1,7 @@ # commpost.nmk: SECOND COMMON FRAGMENT FOR PLATFORMS USING NMAKE -*- makefile -*- # # $Id$ -# Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. # # DESCRIPTION # @@ -203,11 +203,11 @@ $(PFM)\$(VARIETY)\arenacv.exe: $(PFM)\$(VARIETY)\arenacv.obj \ $(PFM)\$(VARIETY)\awlut.exe: $(PFM)\$(VARIETY)\awlut.obj \ $(FMTTESTOBJ) \ - $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) + $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) $(TESTTHROBJ) $(PFM)\$(VARIETY)\awluthe.exe: $(PFM)\$(VARIETY)\awluthe.obj \ $(FMTTESTOBJ) \ - $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) + $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) $(TESTTHROBJ) $(PFM)\$(VARIETY)\awlutth.exe: $(PFM)\$(VARIETY)\awlutth.obj \ $(FMTTESTOBJ) \ @@ -288,9 +288,15 @@ $(PFM)\$(VARIETY)\sacss.exe: $(PFM)\$(VARIETY)\sacss.obj \ $(PFM)\$(VARIETY)\segsmss.exe: $(PFM)\$(VARIETY)\segsmss.obj \ $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) +$(PFM)\$(VARIETY)\sncss.exe: $(PFM)\$(VARIETY)\sncss.obj \ + $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) + $(PFM)\$(VARIETY)\steptest.exe: $(PFM)\$(VARIETY)\steptest.obj \ $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) +$(PFM)\$(VARIETY)\tagtest.exe: $(PFM)\$(VARIETY)\tagtest.obj \ + $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) + $(PFM)\$(VARIETY)\teletest.exe: $(PFM)\$(VARIETY)\teletest.obj \ $(PFM)\$(VARIETY)\mps.lib $(TESTLIBOBJ) @@ -309,6 +315,9 @@ $(PFM)\$(VARIETY)\ztfm.exe: $(PFM)\$(VARIETY)\ztfm.obj \ $(PFM)\$(VARIETY)\mpseventcnv.exe: $(PFM)\$(VARIETY)\eventcnv.obj \ $(PFM)\$(VARIETY)\mps.lib +$(PFM)\$(VARIETY)\mpseventpy.exe: $(PFM)\$(VARIETY)\eventpy.obj \ + $(PFM)\$(VARIETY)\mps.lib + $(PFM)\$(VARIETY)\mpseventtxt.exe: $(PFM)\$(VARIETY)\eventtxt.obj \ $(PFM)\$(VARIETY)\mps.lib @@ -329,6 +338,9 @@ $(PFM)\$(VARIETY)\replaysw.obj: $(PFM)\$(VARIETY)\replay.obj $(PFM)\$(VARIETY)\mpseventcnv.obj: $(PFM)\$(VARIETY)\eventcnv.obj copy $** $@ >nul: +$(PFM)\$(VARIETY)\mpseventpy.obj: $(PFM)\$(VARIETY)\eventpy.obj + copy $** $@ >nul: + $(PFM)\$(VARIETY)\mpseventtxt.obj: $(PFM)\$(VARIETY)\eventtxt.obj copy $** $@ >nul: @@ -379,7 +391,7 @@ $(PFM)\$(VARIETY)\sqlite3.obj: # C. COPYRIGHT AND LICENSE # -# Copyright (C) 2001-2014 Ravenbrook Limited . +# Copyright (c) 2001-2016 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. # diff --git a/mps/code/commpre.nmk b/mps/code/commpre.nmk index e3d2443fe21..f88d2a08a72 100644 --- a/mps/code/commpre.nmk +++ b/mps/code/commpre.nmk @@ -94,7 +94,9 @@ TEST_TARGETS=\ qs.exe \ sacss.exe \ segsmss.exe \ + sncss.exe \ steptest.exe \ + tagtest.exe \ teletest.exe \ walkt0.exe \ zcoll.exe \ @@ -103,7 +105,7 @@ TEST_TARGETS=\ # Stand-alone programs go in EXTRA_TARGETS if they should always be # built, or in OPTIONAL_TARGETS if they should only be built if -EXTRA_TARGETS=mpseventcnv.exe mpseventtxt.exe +EXTRA_TARGETS=mpseventcnv.exe mpseventpy.exe mpseventtxt.exe OPTIONAL_TARGETS=mpseventsql.exe # This target records programs that we were once able to build but @@ -152,11 +154,10 @@ MPMCOMMON=\ [poolmfs] \ [poolmrg] \ [poolmv2] \ - [poolmv] \ [protocol] \ [range] \ + [rangetree] \ [ref] \ - [reserv] \ [ring] \ [root] \ [sa] \ diff --git a/mps/code/config.h b/mps/code/config.h index 4fa4c0b3979..80789d8c069 100644 --- a/mps/code/config.h +++ b/mps/code/config.h @@ -1,7 +1,7 @@ /* config.h: MPS CONFIGURATION * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * Portions copyright (c) 2002 Global Graphics Software. * * PURPOSE @@ -106,8 +106,6 @@ #if defined(CONFIG_STATS) /* CONFIG_STATS = STATISTICS = METERs */ -/* WARNING: this may change the size and fields of MPS structs */ -/* (...but see STATISTIC_DECL, which is invariant) */ #define STATISTICS #define MPS_STATS_STRING "stats" #else @@ -169,8 +167,9 @@ /* 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. + * execution only, where locks are not needed and so the generic + * ("ANSI") lock module lockan.c can be used instead of the + * platform-specific lock module. */ #if !defined(CONFIG_THREAD_SINGLE) @@ -279,8 +278,20 @@ #define ATTRIBUTE_NO_SANITIZE_ADDRESS #endif +/* Attribute for functions that must not be inlined. + * GCC: + * MSVC: + */ +#if defined(MPS_BUILD_GC) || defined(MPS_BUILD_LL) +#define ATTRIBUTE_NOINLINE __attribute__((__noinline__)) +#elif defined(MPS_BUILD_MV) +#define ATTRIBUTE_NOINLINE __declspec(noinline) +#else +#define ATTRIBUTE_NOINLINE +#endif + /* Attribute for functions that do not return. - * GCC: + * GCC: * Clang: */ #if defined(MPS_BUILD_GC) || defined(MPS_BUILD_LL) @@ -290,7 +301,7 @@ #endif /* Attribute for functions that may be unused in some build configurations. - * GCC: + * GCC: * * This attribute must be applied to all Check functions, otherwise * the RASH variety fails to compile with -Wunused-function. (It @@ -304,12 +315,20 @@ #endif -/* EPVMDefaultSubsequentSegSIZE is a default for the alignment of - * subsequent segments (non-initial at each save level) in EPVM. See - * design.mps.poolepvm.arch.segment.size. +/* Compiler extensions */ + +/* LIKELY -- likely conditions + * + * Use to annotate conditions that are likely to be true, such as + * assertions, to help move unlikely code out-of-line. See + * . */ -#define EPVMDefaultSubsequentSegSIZE ((Size)64 * 1024) +#if defined(MPS_BUILD_GC) || defined(MPS_BUILD_LL) +#define LIKELY(exp) __builtin_expect((exp) != 0, 1) +#else +#define LIKELY(exp) ((exp) != 0) +#endif /* Buffer Configuration -- see */ @@ -357,14 +376,6 @@ #define LO_GEN_DEFAULT 0 -/* 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) - - /* Pool MFS Configuration -- see */ #define MFS_EXTEND_BY_DEFAULT ((Size)65536) @@ -396,8 +407,6 @@ #define ArenaPollALLOCTIME (65536.0) -#define ARENA_ZONESHIFT ((Shift)20) - /* .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. */ @@ -408,6 +417,13 @@ #define ARENA_SPARE_DEFAULT 0.75 +/* ARENA_DEFAULT_PAUSE_TIME is the maximum time (in seconds) that + * operations within the arena may pause the mutator for. The default + * is set for typical human interaction. See mps_arena_pause_time_set + * in the manual. */ + +#define ARENA_DEFAULT_PAUSE_TIME (0.1) + #define ARENA_DEFAULT_ZONED TRUE /* ARENA_MINIMUM_COLLECTABLE_SIZE is the minimum size (in bytes) of @@ -417,8 +433,9 @@ #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. */ + * collection rate (in work per second; see ), for + * use in the case where there isn't enough data to use a measured + * value. */ #define ARENA_DEFAULT_COLLECTION_RATE (25000000.0) @@ -459,10 +476,17 @@ #define VM_ARENA_SIZE_DEFAULT ((Size)1 << 28) -/* Stack configuration -- see */ +/* Locus configuration -- see */ + +/* Weighting for the current observation, in the exponential moving + * average computation of the mortality of a generation. */ +#define LocusMortalityALPHA (0.4) + + +/* Stack probe configuration -- see */ /* Currently StackProbe has a useful implementation only on Windows. */ -#if defined(MPS_OS_W3) +#if defined(MPS_OS_W3) && !defined(CONFIG_PF_ANSI) /* See for a justification of this value. */ #define StackProbeDEPTH ((Size)500) #else @@ -472,8 +496,8 @@ /* Shield Configuration -- see */ -#define ShieldCacheSIZE ((size_t)16) -#define ShieldDepthWIDTH (4) +#define ShieldQueueLENGTH 512 /* initial length of shield queue */ +#define ShieldDepthWIDTH 4 /* log2(max nested exposes + 1) */ /* VM Configuration -- see */ @@ -491,10 +515,10 @@ * 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 + * lockix.c pthread_mutexattr_settype _XOPEN_SOURCE >= 500 * prmcix.h stack_t, siginfo_t _XOPEN_SOURCE + * prmclii3.c REG_EAX etc. _GNU_SOURCE + * prmclii6.c REG_RAX etc. _GNU_SOURCE * pthrdext.c sigaction etc. _XOPEN_SOURCE * vmix.c MAP_ANON _GNU_SOURCE * @@ -523,14 +547,14 @@ #endif -/* .feature.xc: OS X feature specification +/* .feature.xc: macOS feature specification * * The MPS needs the following symbols which are not defined by default * * Source Symbols Header Feature * =========== ========================= ============= ==================== - * prmci3li.c __eax etc. _XOPEN_SOURCE - * prmci6li.c __rax etc. _XOPEN_SOURCE + * prmclii3.c __eax etc. _XOPEN_SOURCE + * prmclii6.c __rax etc. _XOPEN_SOURCE * * It is not possible to localize these feature specifications around * the individual headers: all headers share a common set of features @@ -547,21 +571,6 @@ #endif -/* Protection Configuration see - - For each architecture/OS that uses protix.c or protsgix.c, we need to - define what signal number to use, and what si_code value to check. -*/ - -#if defined(MPS_OS_FR) -#define PROT_SIGNAL (SIGSEGV) -#endif - -#if defined(MPS_OS_FR) -#define PROT_SIGINFO_GOOD(info) ((info)->si_code == SEGV_ACCERR) -#endif - - /* Almost all of protxc.c etc. are architecture-independent, but unfortunately the Mach headers don't provide architecture neutral symbols for simple things like thread states. These definitions fix that. */ @@ -581,12 +590,35 @@ #else -#error "Unknown OS X architecture" +#error "Unknown macOS architecture" #endif #endif +/* POSIX thread extensions configuration -- see */ + +#if defined(MPS_OS_LI) || defined(MPS_OS_FR) + +/* PTHREADEXT_SIGSUSPEND -- signal used to suspend a thread + * See + */ +#if defined(CONFIG_PTHREADEXT_SIGSUSPEND) +#define PTHREADEXT_SIGSUSPEND CONFIG_PTHREADEXT_SIGSUSPEND +#else +#define PTHREADEXT_SIGSUSPEND SIGXFSZ +#endif + +/* PTHREADEXT_SIGRESUME -- signal used to resume a thread + * See + */ +#if defined(CONFIG_PTHREADEXT_SIGRESUME) +#define PTHREADEXT_SIGRESUME CONFIG_PTHREADEXT_SIGRESUME +#else +#define PTHREADEXT_SIGRESUME SIGXCPU +#endif + +#endif /* Tracer Configuration -- see */ @@ -657,12 +689,31 @@ } +/* Write barrier deferral + * + * See design.mps.write-barrier.deferral. + * + * TODO: These settings were determined by trial and error, but should + * be based on measurement of the protection overhead on each + * platform. We know it's extremely different between macOS and + * Windows, for example. See design.mps.write-barrier.improv.by-os. + * + * TODO: Consider basing the count on the amount of time that has + * passed in the mutator rather than the number of scans. + */ + +#define WB_DEFER_BITS 2 /* bitfield width for deferral count */ +#define WB_DEFER_INIT 3 /* boring scans after new segment */ +#define WB_DEFER_DELAY 3 /* boring scans after interesting scan */ +#define WB_DEFER_HIT 1 /* boring scans after barrier hit */ + + #endif /* config_h */ /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/dbgpool.c b/mps/code/dbgpool.c index 6a8417595f7..b2e7bdb433d 100644 --- a/mps/code/dbgpool.c +++ b/mps/code/dbgpool.c @@ -1,7 +1,7 @@ /* dbgpool.c: POOL DEBUG MIXIN * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. * * .source: design.mps.object-debug @@ -87,7 +87,7 @@ Bool PoolDebugMixinCheck(PoolDebugMixin debug) /* DebugPoolDebugMixin -- gets the debug mixin, if any */ -#define DebugPoolDebugMixin(pool) (((pool)->class->debugMixin)(pool)) +#define DebugPoolDebugMixin(pool) (Method(Pool, pool, debugMixin)(pool)) /* PoolNoDebugMixin -- debug mixin methods for pools with no mixin */ @@ -127,7 +127,7 @@ static PoolDebugOptionsStruct debugPoolOptionsDefault = { "POST", 4, "DEAD", 4, }; -static Res DebugPoolInit(Pool pool, ArgList args) +static Res DebugPoolInit(Pool pool, Arena arena, PoolClass klass, ArgList args) { Res res; PoolDebugOptions options = &debugPoolOptionsDefault; @@ -136,7 +136,10 @@ static Res DebugPoolInit(Pool pool, ArgList args) Size tagSize; ArgStruct arg; - AVERT(Pool, pool); + AVER(pool != NULL); + AVERT(Arena, arena); + AVERT(PoolClass, klass); + AVERT(ArgList, args); if (ArgPick(&arg, args, MPS_KEY_POOL_DEBUG_OPTIONS)) options = (PoolDebugOptions)arg.val.pool_debug_options; @@ -147,10 +150,11 @@ static Res DebugPoolInit(Pool pool, ArgList args) /* not been published yet. */ tagInit = NULL; tagSize = 0; - res = SuperclassOfPool(pool)->init(pool, args); + res = SuperclassPoly(Pool, klass)->init(pool, arena, klass, args); if (res != ResOK) return res; + SetClassOfPoly(pool, klass); debug = DebugPoolDebugMixin(pool); AVER(debug != NULL); @@ -202,7 +206,7 @@ static Res DebugPoolInit(Pool pool, ArgList args) return ResOK; tagFail: - SuperclassOfPool(pool)->finish(pool); + SuperclassPoly(Inst, klass)->finish(MustBeA(Inst, pool)); AVER(res != ResOK); return res; } @@ -210,9 +214,11 @@ static Res DebugPoolInit(Pool pool, ArgList args) /* DebugPoolFinish -- finish method for a debug pool */ -static void DebugPoolFinish(Pool pool) +static void DebugPoolFinish(Inst inst) { + Pool pool = MustBeA(AbstractPool, inst); PoolDebugMixin debug; + PoolClass klass; AVERT(Pool, pool); @@ -223,7 +229,8 @@ static void DebugPoolFinish(Pool pool) SplayTreeFinish(&debug->index); PoolDestroy(debug->tagPool); } - SuperclassOfPool(pool)->finish(pool); + klass = ClassOfPoly(Pool, pool); + SuperclassPoly(Inst, klass)->finish(inst); } @@ -397,14 +404,16 @@ static Bool freeCheck(PoolDebugMixin debug, Pool pool, Addr base, Addr limit) /* freeCheckAlloc -- allocation wrapper for free-checking */ static Res freeCheckAlloc(Addr *aReturn, PoolDebugMixin debug, Pool pool, - Size size, Bool withReservoir) + Size size) { Res res; Addr new; + PoolClass klass; AVER(aReturn != NULL); - res = SuperclassOfPool(pool)->alloc(&new, pool, size, withReservoir); + klass = ClassOfPoly(Pool, pool); + res = SuperclassPoly(Pool, klass)->alloc(&new, pool, size); if (res != ResOK) return res; if (debug->freeSize != 0) @@ -421,9 +430,11 @@ static Res freeCheckAlloc(Addr *aReturn, PoolDebugMixin debug, Pool pool, static void freeCheckFree(PoolDebugMixin debug, Pool pool, Addr old, Size size) { + PoolClass klass; if (debug->freeSize != 0) freeSplat(debug, pool, old, AddrAdd(old, size)); - SuperclassOfPool(pool)->free(pool, old, size); + klass = ClassOfPoly(Pool, pool); + SuperclassPoly(Pool, klass)->free(pool, old, size); } @@ -445,7 +456,7 @@ static void freeCheckFree(PoolDebugMixin debug, */ static Res fenceAlloc(Addr *aReturn, PoolDebugMixin debug, Pool pool, - Size size, Bool withReservoir) + Size size) { Res res; Addr obj, startFence, clientNew, clientLimit, limit; @@ -458,8 +469,7 @@ static Res fenceAlloc(Addr *aReturn, PoolDebugMixin debug, Pool pool, alignedFenceSize = SizeAlignUp(debug->fenceSize, PoolAlignment(pool)); alignedSize = SizeAlignUp(size, PoolAlignment(pool)); res = freeCheckAlloc(&obj, debug, pool, - alignedSize + 2 * alignedFenceSize, - withReservoir); + alignedSize + 2 * alignedFenceSize); if (res != ResOK) return res; @@ -514,7 +524,7 @@ static void fenceFree(PoolDebugMixin debug, { Size alignedFenceSize, alignedSize; - ASSERT(fenceCheck(debug, pool, old, size), "fencepost check on free"); + ASSERT(fenceCheck(debug, pool, old, size), "fencepost check on free"); /* */ alignedFenceSize = SizeAlignUp(debug->fenceSize, PoolAlignment(pool)); alignedSize = SizeAlignUp(size, PoolAlignment(pool)); @@ -526,7 +536,7 @@ static void fenceFree(PoolDebugMixin debug, /* tagAlloc -- allocation wrapper for tagged pools */ static Res tagAlloc(PoolDebugMixin debug, - Pool pool, Addr new, Size size, Bool withReservoir) + Pool pool, Addr new, Size size) { Tag tag; Res res; @@ -534,15 +544,9 @@ static Res tagAlloc(PoolDebugMixin debug, Addr addr; UNUSED(pool); - res = PoolAlloc(&addr, debug->tagPool, debug->tagSize, FALSE); - if (res != ResOK) { - if (withReservoir) { /* missingTags++; - return ResOK; - } else { - return res; - } - } + res = PoolAlloc(&addr, debug->tagPool, debug->tagSize); + if (res != ResOK) + return res; tag = (Tag)addr; tag->addr = new; tag->size = size; TreeInit(TagTree(tag)); @@ -585,8 +589,7 @@ static void tagFree(PoolDebugMixin debug, Pool pool, Addr old, Size size) * Eventually, tag init args will need to be handled somewhere here. */ -static Res DebugPoolAlloc(Addr *aReturn, - Pool pool, Size size, Bool withReservoir) +static Res DebugPoolAlloc(Addr *aReturn, Pool pool, Size size) { Res res; Addr new = NULL; /* suppress "may be used uninitialized" warning */ @@ -595,20 +598,19 @@ static Res DebugPoolAlloc(Addr *aReturn, AVER(aReturn != NULL); AVERT(Pool, pool); AVER(size > 0); - AVERT(Bool, withReservoir); debug = DebugPoolDebugMixin(pool); AVER(debug != NULL); AVERT(PoolDebugMixin, debug); if (debug->fenceSize != 0) - res = fenceAlloc(&new, debug, pool, size, withReservoir); + res = fenceAlloc(&new, debug, pool, size); else - res = freeCheckAlloc(&new, debug, pool, size, withReservoir); + res = freeCheckAlloc(&new, debug, pool, size); if (res != ResOK) return res; /* Allocate object first, so it fits even when the tag doesn't. */ if (debug->tagInit != NULL) { - res = tagAlloc(debug, pool, new, size, withReservoir); + res = tagAlloc(debug, pool, new, size); if (res != ResOK) goto tagFail; } @@ -737,7 +739,7 @@ void DebugPoolFreeCheck(Pool pool, Addr base, Addr limit) AVERT(PoolDebugMixin, debug); if (debug->freeSize != 0) ASSERT(freeCheck(debug, pool, base, limit), - "free space corrupted on release"); + "free space corrupted on release"); /* */ } } @@ -771,19 +773,19 @@ void DebugPoolCheckFreeSpace(Pool pool) /* PoolClassMixInDebug -- mix in the debug support for class init */ -void PoolClassMixInDebug(PoolClass class) +void PoolClassMixInDebug(PoolClass klass) { - /* Can't check class because it's not initialized yet */ - class->init = DebugPoolInit; - class->finish = DebugPoolFinish; - class->alloc = DebugPoolAlloc; - class->free = DebugPoolFree; + /* Can't check klass because it's not initialized yet */ + klass->instClassStruct.finish = DebugPoolFinish; + klass->init = DebugPoolInit; + klass->alloc = DebugPoolAlloc; + klass->free = DebugPoolFree; } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/dbgpool.h b/mps/code/dbgpool.h index e01d8c3b650..051582121b0 100644 --- a/mps/code/dbgpool.h +++ b/mps/code/dbgpool.h @@ -59,7 +59,7 @@ extern Bool PoolDebugOptionsCheck(PoolDebugOptions opt); extern Bool PoolDebugMixinCheck(PoolDebugMixin dbg); -extern void PoolClassMixInDebug(PoolClass class); +extern void PoolClassMixInDebug(PoolClass klass); extern void DebugPoolCheckFences(Pool pool); extern void DebugPoolCheckFreeSpace(Pool pool); diff --git a/mps/code/djbench.c b/mps/code/djbench.c index fbc6dcabc1c..c417e6004c5 100644 --- a/mps/code/djbench.c +++ b/mps/code/djbench.c @@ -1,7 +1,7 @@ /* djbench.c -- "DJ" Benchmark on ANSI C library * * $Id$ - * Copyright 2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2013-2018 Ravenbrook Limited. See end of file for license. * * This is an allocation stress benchmark test for manual variable pools * and also for stdlib malloc/free (for comparison). @@ -232,17 +232,18 @@ static struct { } pools[] = { {"mvt", arena_wrap, dj_reserve, mps_class_mvt}, {"mvff", arena_wrap, dj_reserve, mps_class_mvff}, - {"mv", arena_wrap, dj_alloc, mps_class_mv}, - {"mvb", arena_wrap, dj_reserve, mps_class_mv}, /* mv with buffers */ + {"mvffa", arena_wrap, dj_alloc, mps_class_mvff}, /* mvff with alloc */ {"an", wrap, dj_malloc, dummy_class}, }; /* Command-line driver */ -int main(int argc, char *argv[]) { +int main(int argc, char *argv[]) +{ int ch; unsigned i; + mps_bool_t seed_specified = FALSE; seed = rnd_seed(); @@ -274,6 +275,7 @@ int main(int argc, char *argv[]) { break; case 'x': seed = strtoul(optarg, NULL, 10); + seed_specified = TRUE; break; case 'z': zoned = FALSE; @@ -358,8 +360,10 @@ int main(int argc, char *argv[]) { argc -= optind; argv += optind; - printf("seed: %lu\n", seed); - (void)fflush(stdout); + if (!seed_specified) { + printf("seed: %lu\n", seed); + (void)fflush(stdout); + } while (argc > 0) { for (i = 0; i < NELEMS(pools); ++i) @@ -381,7 +385,7 @@ int main(int argc, char *argv[]) { /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2014 Ravenbrook Limited . + * Copyright (c) 2013-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/event.c b/mps/code/event.c index cf9d4e0a8b5..b8f12383b0e 100644 --- a/mps/code/event.c +++ b/mps/code/event.c @@ -1,19 +1,12 @@ /* event.c: EVENT LOGGING * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * * .sources: mps.design.event * * TRANSGRESSIONS (rule.impl.trans) * - * .trans.ref: The reference counting used to destroy the mps_io object - * isn't right. - * - * .trans.log: The log file will be re-created if the lifetimes of - * arenas don't overlap, but shared if they do. mps_io_create cannot - * be called twice, but EventInit avoids this anyway. - * * .trans.ifdef: This file should logically be split into two, event.c * (which contains NOOP definitions, for general use) and eventdl.c, which * is specific to the logging variety and actually does logging (maybe). @@ -24,6 +17,7 @@ #include "mpm.h" #include "event.h" #include "mpsio.h" +#include "lock.h" SRCID(event, "$Id$"); @@ -34,7 +28,6 @@ SRCID(event, "$Id$"); static Bool eventInited = FALSE; static Bool eventIOInited = FALSE; static mps_io_t eventIO; -static Count eventUserCount; static Serial EventInternSerial; /* Buffers in which events are recorded, from the top down. */ @@ -207,25 +200,26 @@ void EventInit(void) AVER(EventBufferSIZE <= EventSizeMAX); /* Only if this is the first call. */ - if(!eventInited) { /* See .trans.log */ - EventKind kind; - for (kind = 0; kind < EventKindLIMIT; ++kind) { - AVER(EventLast[kind] == NULL); - AVER(EventWritten[kind] == NULL); - EventLast[kind] = EventWritten[kind] = EventBuffer[kind] + EventBufferSIZE; + if (!eventInited) { /* See .trans.log */ + LockClaimGlobalRecursive(); + if (!eventInited) { + EventKind kind; + for (kind = 0; kind < EventKindLIMIT; ++kind) { + AVER(EventLast[kind] == NULL); + AVER(EventWritten[kind] == NULL); + EventLast[kind] = EventWritten[kind] = EventBuffer[kind] + EventBufferSIZE; + } + eventInited = TRUE; + EventKindControl = (Word)mps_lib_telemetry_control(); + EventInternSerial = (Serial)1; /* 0 is reserved */ + (void)EventInternString(MPSVersion()); /* emit version */ + EVENT7(EventInit, EVENT_VERSION_MAJOR, EVENT_VERSION_MEDIAN, + EVENT_VERSION_MINOR, EventCodeMAX, EventNameMAX, MPS_WORD_WIDTH, + mps_clocks_per_sec()); + /* flush these initial events to get the first ClockSync out. */ + EventSync(); } - eventUserCount = (Count)1; - eventInited = TRUE; - EventKindControl = (Word)mps_lib_telemetry_control(); - EventInternSerial = (Serial)1; /* 0 is reserved */ - (void)EventInternString(MPSVersion()); /* emit version */ - EVENT7(EventInit, EVENT_VERSION_MAJOR, EVENT_VERSION_MEDIAN, - EVENT_VERSION_MINOR, EventCodeMAX, EventNameMAX, MPS_WORD_WIDTH, - mps_clocks_per_sec()); - /* flush these initial events to get the first ClockSync out. */ - EventSync(); - } else { - ++eventUserCount; + LockReleaseGlobalRecursive(); } } @@ -235,11 +229,8 @@ void EventInit(void) void EventFinish(void) { AVER(eventInited); - AVER(eventUserCount > 0); EventSync(); - - --eventUserCount; } @@ -518,7 +509,7 @@ Res EventWrite(Event event, mps_lib_FILE *stream) } -extern void EventDump(mps_lib_FILE *stream) +void EventDump(mps_lib_FILE *stream) { UNUSED(stream); } @@ -529,7 +520,7 @@ extern void EventDump(mps_lib_FILE *stream) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/eventdef.h b/mps/code/eventdef.h index f121d684bc8..adacf12c548 100644 --- a/mps/code/eventdef.h +++ b/mps/code/eventdef.h @@ -1,7 +1,7 @@ /* -- Event Logging Definitions * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. * * .source: * @@ -36,7 +36,7 @@ */ #define EVENT_VERSION_MAJOR ((unsigned)1) -#define EVENT_VERSION_MEDIAN ((unsigned)5) +#define EVENT_VERSION_MEDIAN ((unsigned)7) #define EVENT_VERSION_MINOR ((unsigned)0) @@ -67,7 +67,7 @@ */ #define EventNameMAX ((size_t)19) -#define EventCodeMAX ((EventCode)0x0086) +#define EventCodeMAX ((EventCode)0x0088) #define EVENT_LIST(EVENT, X) \ /* 0123456789012345678 <- don't exceed without changing EventNameMAX */ \ @@ -93,8 +93,8 @@ EVENT(X, SegFree , 0x0014, TRUE, Seg) \ EVENT(X, PoolInit , 0x0015, TRUE, Pool) \ EVENT(X, PoolFinish , 0x0016, TRUE, Pool) \ - EVENT(X, PoolAlloc , 0x0017, TRUE, Object) \ - EVENT(X, PoolFree , 0x0018, TRUE, Object) \ + EVENT(X, PoolAlloc , 0x0017, FALSE, Object) \ + EVENT(X, PoolFree , 0x0018, FALSE, Object) \ EVENT(X, LandInit , 0x0019, TRUE, Pool) \ EVENT(X, Intern , 0x001a, TRUE, User) \ EVENT(X, Label , 0x001b, TRUE, User) \ @@ -138,7 +138,7 @@ EVENT(X, TraceScanSeg , 0x003C, TRUE, Seg) \ /* TraceScanSingleRef abuses kind, see .kind.abuse */ \ EVENT(X, TraceScanSingleRef , 0x003D, TRUE, Seg) \ - EVENT(X, TraceStatCondemn , 0x003E, TRUE, Trace) \ + /* EVENT(X, TraceStatCondemn , 0x003E, TRUE, Trace) */ \ EVENT(X, TraceStatScan , 0x003F, TRUE, Trace) \ EVENT(X, TraceStatFix , 0x0040, TRUE, Trace) \ EVENT(X, TraceStatReclaim , 0x0041, TRUE, Trace) \ @@ -160,7 +160,7 @@ /* PoolPush/Pop go under Object, because they're user ops. */ \ /* EVENT(X, PoolPush , 0x0060, TRUE, Object) */ \ /* EVENT(X, PoolPop , 0x0061, TRUE, Object) */ \ - EVENT(X, ReservoirLimitSet , 0x0062, TRUE, Arena) \ + /* EVENT(X, ReservoirLimitSet , 0x0062, TRUE, Arena) */ \ EVENT(X, CommitLimitSet , 0x0063, TRUE, Arena) \ EVENT(X, ArenaSetSpare , 0x0064, TRUE, Arena) \ EVENT(X, ArenaAlloc , 0x0065, TRUE, Arena) \ @@ -186,13 +186,15 @@ EVENT(X, ArenaSetEmergency , 0x0078, TRUE, Arena) \ EVENT(X, VMCompact , 0x0079, TRUE, Arena) \ EVENT(X, amcScanNailed , 0x0080, TRUE, Seg) \ - EVENT(X, AMCTraceEnd , 0x0081, TRUE, Trace) \ + /* EVENT(X, AMCTraceEnd , 0x0081, TRUE, Trace) */ \ EVENT(X, TraceCreatePoolGen , 0x0082, TRUE, Trace) \ /* new events for performance analysis of large heaps. */ \ - EVENT(X, TraceCondemnZones , 0x0083, TRUE, Trace) \ + /* 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) */ \ + EVENT(X, PauseTimeSet , 0x0087, TRUE, Arena) \ + EVENT(X, TraceEndGen , 0x0088, TRUE, Trace) /* Remember to update EventNameMAX and EventCodeMAX above! @@ -442,15 +444,6 @@ PARAM(X, 2, P, arena) \ PARAM(X, 3, A, refIO) -#define EVENT_TraceStatCondemn_PARAMS(PARAM, X) \ - PARAM(X, 0, P, trace) \ - PARAM(X, 1, W, condemned) \ - PARAM(X, 2, W, notCondemned) \ - PARAM(X, 3, W, foundation) \ - PARAM(X, 4, W, rate) \ - PARAM(X, 5, D, mortality) \ - PARAM(X, 6, D, finishingTime) - #define EVENT_TraceStatScan_PARAMS(PARAM, X) \ PARAM(X, 0, P, trace) \ PARAM(X, 1, W, rootScanCount) \ @@ -551,10 +544,6 @@ PARAM(X, 2, B, isMutator) \ PARAM(X, 3, U, rank) -#define EVENT_ReservoirLimitSet_PARAMS(PARAM, X) \ - PARAM(X, 0, P, arena) \ - PARAM(X, 1, W, size) - #define EVENT_CommitLimitSet_PARAMS(PARAM, X) \ PARAM(X, 0, P, arena) \ PARAM(X, 1, W, limit) \ @@ -583,8 +572,7 @@ #define EVENT_SegMerge_PARAMS(PARAM, X) \ PARAM(X, 0, P, segLo) \ - PARAM(X, 1, P, segHi) \ - PARAM(X, 2, B, withReservoirPermit) + PARAM(X, 1, P, segHi) #define EVENT_SegSplit_PARAMS(PARAM, X) \ PARAM(X, 0, P, seg) \ @@ -655,7 +643,7 @@ #define EVENT_ArenaPoll_PARAMS(PARAM, X) \ PARAM(X, 0, P, arena) \ PARAM(X, 1, W, start) \ - PARAM(X, 2, W, quanta) + PARAM(X, 2, B, workWasDone) #define EVENT_ArenaSetEmergency_PARAMS(PARAM, X) \ PARAM(X, 0, P, arena) \ @@ -674,7 +662,7 @@ PARAM(X, 4, W, notCondemned) /* collectible but not condemned bytes */ \ PARAM(X, 5, W, foundation) /* foundation size */ \ PARAM(X, 6, W, white) /* white reference set */ \ - PARAM(X, 7, W, rate) /* segs to scan per increment */ + PARAM(X, 7, W, quantumWork) /* tracing work to be done in each poll */ #define EVENT_VMCompact_PARAMS(PARAM, X) \ PARAM(X, 0, W, vmem0) /* pre-collection reserved size */ \ @@ -689,30 +677,6 @@ PARAM(X, 4, W, fixed) /* scan state fixed summary */ \ PARAM(X, 5, W, refset) /* scan state refset */ -#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, 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) \ - PARAM(X, 6, W, pRet) \ - PARAM(X, 7, W, pCS) \ - PARAM(X, 8, W, pRS) \ - PARAM(X, 9, W, sCM) \ - PARAM(X, 10, W, pCM) \ - PARAM(X, 11, W, sRM) \ - PARAM(X, 12, W, pRM) \ - PARAM(X, 13, W, pRM1) \ - PARAM(X, 14, W, pRMrr) \ - PARAM(X, 15, W, pRMr1) \ - PARAM(X, 16, W, sCL) \ - PARAM(X, 17, W, pCL) \ - PARAM(X, 18, W, sRL) \ - PARAM(X, 19, W, pRL) \ - PARAM(X, 20, W, pRLr) - #define EVENT_TraceCreatePoolGen_PARAMS(PARAM, X) \ PARAM(X, 0, P, gendesc) /* generation description */ \ PARAM(X, 1, W, capacity) /* capacity of generation */ \ @@ -726,11 +690,6 @@ 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 */ \ - PARAM(X, 1, W, condemnedSet) /* the condemned zoneSet */ \ - PARAM(X, 2, W, white) /* the trace's white zoneSet */ - #define EVENT_ArenaGenZoneAdd_PARAMS(PARAM, X) \ PARAM(X, 0, P, arena) /* the arena */ \ PARAM(X, 1, P, gendesc) /* the generation description */ \ @@ -740,12 +699,24 @@ PARAM(X, 0, P, arena) /* the arena */ \ PARAM(X, 1, W, zoneSet) /* zones that aren't free any longer */ +#define EVENT_PauseTimeSet_PARAMS(PARAM, X) \ + PARAM(X, 0, P, arena) /* the arena */ \ + PARAM(X, 1, D, pauseTime) /* the new maximum pause time, in seconds */ + +#define EVENT_TraceEndGen_PARAMS(PARAM, X) \ + PARAM(X, 0, P, trace) /* the trace */ \ + PARAM(X, 1, P, gen) /* the generation */ \ + PARAM(X, 2, W, condemned) /* bytes condemned in generation */ \ + PARAM(X, 3, W, forwarded) /* bytes forwarded from generation */ \ + PARAM(X, 4, W, preservedInPlace) /* bytes preserved in generation */ \ + PARAM(X, 5, D, mortality) /* updated mortality */ + #endif /* eventdef_h */ /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/eventpy.c b/mps/code/eventpy.c new file mode 100644 index 00000000000..feac0efefb5 --- /dev/null +++ b/mps/code/eventpy.c @@ -0,0 +1,103 @@ +/* eventpy.c: GENERATE PYTHON INTERFACE TO EVENTS + * + * $Id$ + * Copyright (c) 2016-2018 Ravenbrook Limited. See end of file for license. + * + * This command-line program emits Python data structures that can be + * used to parse an event stream in text format (as output by the + * mpseventcnv program). + */ + +#include /* printf, puts */ + +#include "event.h" + +int main(int argc, char *argv[]) +{ + UNUSED(argc); + UNUSED(argv); + + puts("from collections import namedtuple"); + + printf("__version__ = %d, %d, %d\n", EVENT_VERSION_MAJOR, + EVENT_VERSION_MEDIAN, EVENT_VERSION_MINOR); + + puts("KindDesc = namedtuple('KindDesc', 'name code doc')"); + puts("class Kind:"); +#define ENUM(_, NAME, DOC) \ + printf(" " #NAME " = KindDesc('" #NAME "', %d, \"%s\")\n", \ + EventKind ## NAME, DOC); + EventKindENUM(ENUM, _); +#undef ENUM + + puts("KIND = {"); +#define ENUM(_, NAME, _1) \ + printf(" %d: Kind." #NAME ",\n", EventKind ## NAME); + EventKindENUM(ENUM, _); +#undef ENUM + puts("}"); + + puts("EventParam = namedtuple('EventParam', 'sort name')"); + puts("EventDesc = namedtuple('EventDesc', 'name code always kind params')"); + puts("class Event:"); +#define EVENT_PARAM(X, INDEX, SORT, NAME) \ + puts(" EventParam('" #SORT "', '" #NAME "'),"); +#define EVENT_DEFINE(X, NAME, CODE, ALWAYS, KIND) \ + printf(" " #NAME " = EventDesc('" #NAME "', %d, %s, Kind." #KIND ", [\n", \ + CODE, ALWAYS ? "True" : "False"); \ + EVENT_ ## NAME ## _PARAMS(EVENT_PARAM, X); \ + puts(" ])"); + EVENT_LIST(EVENT_DEFINE, 0); +#undef EVENT + + puts("EVENT = {"); +#define EVENT_ITEM(X, NAME, CODE, ALWAYS, KIND) \ + printf(" %d: Event." #NAME ",\n", CODE); + EVENT_LIST(EVENT_ITEM, 0); +#undef EVENT + puts("}"); + + return 0; +} + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (c) 2016-2018 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/mps/code/eventrep.c b/mps/code/eventrep.c deleted file mode 100644 index c363010bea7..00000000000 --- a/mps/code/eventrep.c +++ /dev/null @@ -1,693 +0,0 @@ -/* eventrep.c: Allocation replayer routines - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. - * - * $Id$ - */ - -#include "config.h" -/* override variety setting for EVENT */ -#define EVENT - -#include "eventcom.h" -#include "eventrep.h" -#include "eventpro.h" -#include "mpmtypes.h" - -#include "mps.h" -#include "mpsavm.h" -#include "mpsacl.h" -#include "mpscmv.h" -#include "mpscmvff.h" -#include "mpscepvm.h" -#include "fmtpstst.h" -#include "mpscepdl.h" - -#include "table.h" - -#include /* for size_t */ -#include /* for va_list */ -#include /* for EXIT_FAILURE */ -#include /* for printf */ -#include "mpstd.h" - - -#if defined(MPS_OS_W3) && defined(MPS_ARCH_I6) -#define PRIuLONGEST "llu" -#define PRIXPTR "016llX" -typedef unsigned long long ulongest_t; -#else -#define PRIuLONGEST "lu" -#define PRIXPTR "08lX" -typedef unsigned long ulongest_t; -#endif - - -typedef unsigned long ulong; - - -/* Globals */ - -static ulong totalEvents; /* count of events */ -static ulong discardedEvents; /* count of ignored events */ -static ulong unknownEvents; /* count of unknown events */ - -static Word eventTime; - -/* Dictionaries for translating from log to replay values */ -static Table arenaTable; /* dictionary of arenas */ -static Table poolTable; /* dictionary of poolReps */ -static Table apTable; /* dictionary of apReps */ - - -/* poolSupport -- describes pool support for explicit deallocation */ - -enum {supportTruncate = 1, supportFree, supportNothing}; -typedef int poolSupport; - - -/* objectTable -- object address mapping structure - * - * .obj-mapping.truncate: Pools that support truncate need to keep track - * of object end points as well. .obj-mapping.partial-free: Arbitrary - * partial free is not supported. - */ - -typedef struct objectTableStruct { - Table startTable; - Table endTable; -} objectTableStruct; -typedef struct objectTableStruct *objectTable; - - -/* poolRep -- pool tracking structure - * - * .pool.object-addr: Pools that support explicit free (or truncate) - * need to maintain a mapping from the addresses in the log to those in - * the replay. - * - * .bufclass: In order to create APs with the correct arguments, the - * replayer has to pick the right BufferInit event to use, as there's - * one for each superclass. The pool determines the buffer class, so - * we store its subclass level in the pool representation. - */ - -typedef struct poolRepStruct { - mps_pool_t pool; /* the replay pool */ - objectTable objects; - int bufferClassLevel; /* subclass level of the buffer class */ -} poolRepStruct; -typedef struct poolRepStruct *poolRep; - - -/* apRep -- ap tracking structure */ - -typedef struct apRepStruct { - mps_ap_t ap; /* the replay ap */ - objectTable objects; /* object mapping for the pool of this ap */ -} apRepStruct; -typedef struct apRepStruct *apRep; - - -/* PointerAdd -- add offset to pointer */ - -#define PointerAdd(p, s) ((void *)((char *)(p) + (s))) -#define PointerSub(p, s) ((void *)((char *)(p) - (s))) - - -/* error -- error signalling */ - -ATTRIBUTE_FORMAT((printf, 1, 2)) -static void error(const char *format, ...) -{ - va_list args; - - fflush(stdout); /* sync */ - fprintf(stderr, "Failed @%"PRIuLONGEST" ", (ulongest_t)eventTime); - va_start(args, format); - vfprintf(stderr, format, args); - fprintf(stderr, "\n"); - va_end(args); - exit(EXIT_FAILURE); -} - - -/* verify, verifyMPS -- check return values - * - * We don't use assert for this, because we want it in release as well. - */ - -#define verifyMPS(res) \ - MPS_BEGIN if ((res) != MPS_RES_OK) error("line %d MPS", __LINE__); MPS_END - -#define verify(cond) \ - MPS_BEGIN if (!(cond)) error("line %d " #cond, __LINE__); MPS_END - - -/* objectTableCreate -- create an objectTable */ - -static objectTable objectTableCreate(poolSupport support) -{ - if (support != supportNothing) { - Res ires; - objectTable table; - - table = malloc(sizeof(objectTableStruct)); - verify(table != NULL); - ires = TableCreate(&table->startTable, (size_t)1<<12); - verify(ires == ResOK); - if (support == supportTruncate) { - ires = TableCreate(&table->endTable, (size_t)1<<12); - verify(ires == ResOK); - } else { - table->endTable = NULL; - } - return table; - } else { - return NULL; - } -} - - -/* objectTableDestroy -- destroy an objectTable */ - -static void objectTableDestroy(objectTable table) -{ - if (table != NULL) { - TableDestroy(table->startTable); - if (table->endTable != NULL) - TableDestroy(table->endTable); - free(table); - } -} - - -/* objDefine -- add a new mapping to an objectTable */ - -static void objDefine(objectTable table, - void *logObj, void *obj, size_t size) -{ - if (table != NULL) { - Res ires; - - ires = TableDefine(table->startTable, (TableKey)logObj, obj); - verify(ires == ResOK); - if (table->endTable != NULL) { - ires = TableDefine(table->endTable, - (TableKey)PointerAdd(logObj, size), - PointerAdd(obj, size)); - verify(ires == ResOK); - } - } -} - - -/* objRemove -- look up and remove a mapping in an objectTable */ - -static void objRemove(void **objReturn, objectTable table, - void *logObj, size_t size) -{ - Bool found; - Res ires; - void *obj; - void *end; - void *logEnd; - - found = TableLookup(&obj, table->startTable, (TableKey)logObj); - if (found) { - ires = TableRemove(table->startTable, (TableKey)logObj); - verify(ires == ResOK); - if (table->endTable != NULL) { - ires = TableRemove(table->endTable, - (TableKey)PointerAdd(logObj, size)); - verify(ires == ResOK); - } - *objReturn = obj; - return; - } - /* Must be a truncation. */ - verify(table->endTable != NULL); - logEnd = PointerAdd(logObj, size); - found = TableLookup(&end, table->endTable, (TableKey)logEnd); - verify(found); - obj = PointerSub(end, size); - /* Remove the old end and insert the new one. */ - ires = TableRemove(table->endTable, (TableKey)logEnd); - verify(ires == ResOK); - ires = TableDefine(table->endTable, (TableKey)logObj, obj); - verify(ires == ResOK); - *objReturn = obj; - return; -} - - -/* poolRecreate -- create and record a pool */ - -static void poolRecreate(void *logPool, void *logArena, - mps_pool_class_t pool_class, - poolSupport support, int bufferClassLevel, ...) -{ - va_list args; - mps_pool_t pool; - mps_res_t eres; - poolRep rep; - Res ires; - void *entry; - Bool found; - - found = TableLookup(&entry, arenaTable, (TableKey)logArena); - verify(found); - va_start(args, bufferClassLevel); - eres = mps_pool_create_v(&pool, (mps_arena_t)entry, class, args); - verifyMPS(eres); - va_end(args); - rep = malloc(sizeof(poolRepStruct)); - verify(rep != NULL); - rep->pool = pool; - rep->objects = objectTableCreate(support); - rep->bufferClassLevel = bufferClassLevel; - ires = TableDefine(poolTable, (TableKey)logPool, (void *)rep); - verify(ires == ResOK); -} - - -/* poolRedestroy -- destroy and derecord a pool */ - -static void poolRedestroy(void *logPool) -{ - Res ires; - void *entry; - Bool found; - poolRep rep; - - found = TableLookup(&entry, poolTable, (TableKey)logPool); - verify(found); - rep = (poolRep)entry; - mps_pool_destroy(rep->pool); - ires = TableRemove(poolTable, (TableKey)logPool); - verify(ires == ResOK); - objectTableDestroy(rep->objects); - free(rep); -} - - -/* apRecreate -- create and record an ap */ - -static void apRecreate(void *logAp, void *logPool, ...) -{ - va_list args; - mps_ap_t ap; - poolRep pRep; - apRep aRep; - mps_res_t eres; - Res ires; - void *entry; - Bool found; - - found = TableLookup(&entry, poolTable, (TableKey)logPool); - verify(found); - pRep = (poolRep)entry; - va_start(args, logPool); - eres = mps_ap_create_v(&ap, pRep->pool, args); - verifyMPS(eres); - va_end(args); - aRep = malloc(sizeof(apRepStruct)); - verify(aRep != NULL); - aRep->ap = ap; - aRep->objects = pRep->objects; - ires = TableDefine(apTable, (TableKey)logAp, (void *)aRep); - verify(ires == ResOK); -} - - -/* apRedestroy -- destroy and derecord an ap */ - -static void apRedestroy(void *logAp) -{ - Res ires; - void *entry; - Bool found; - apRep rep; - - found = TableLookup(&entry, apTable, (TableKey)logAp); - verify(found); - rep = (apRep)entry; - mps_ap_destroy(rep->ap); - ires = TableRemove(apTable, (TableKey)logAp); - verify(ires == ResOK); - free(rep); -} - - -/* EventReplay -- replay event */ - -static arenaJustCreated = FALSE; - -void EventReplay(Event event, Word etime) -{ - mps_res_t eres; - Res ires; - Bool found; - void *entry; - - ++totalEvents; - eventTime = etime; - switch(event->any.code) { - case EventArenaCreateVM: { /* arena, userSize, chunkSize */ - mps_arena_t arena; - - eres = mps_arena_create(&arena, mps_arena_class_vm(), - event->pww.w1); - verifyMPS(eres); - ires = TableDefine(arenaTable, (TableKey)event->pww.p0, (void *)arena); - verify(ires == ResOK); - arenaJustCreated = TRUE; - } break; - case EventArenaCreateVMNZ: { /* arena, userSize, chunkSize */ - mps_arena_t arena; - - eres = mps_arena_create(&arena, mps_arena_class_vmnz(), - event->pww.w1); - verifyMPS(eres); - ires = TableDefine(arenaTable, (TableKey)event->pww.p0, (void *)arena); - verify(ires == ResOK); - arenaJustCreated = TRUE; - } break; - case EventArenaCreateCL: { /* arena, size, base */ - mps_arena_t arena; - void *base; - - base = malloc((size_t)event->pwa.w1); - verify(base != NULL); - eres = mps_arena_create(&arena, mps_arena_class_cl(), - (Size)event->pwa.w1, base); - verifyMPS(eres); - ires = TableDefine(arenaTable, (TableKey)event->pw.p0, (void *)arena); - verify(ires == ResOK); - arenaJustCreated = TRUE; - } break; - case EventArenaDestroy: { /* arena */ - found = TableLookup(&entry, arenaTable, (TableKey)event->p.p0); - verify(found); - mps_arena_destroy((mps_arena_t)entry); - ires = TableRemove(arenaTable, (TableKey)event->pw.p0); - verify(ires == ResOK); - } break; - case EventPoolInitMVFF: { - /* pool, arena, extendBy, avgSize, align, slotHigh, arenaHigh, firstFit */ - poolRecreate(event->ppwwwuuu.p0, event->ppwwwuuu.p1, - mps_class_mvff(), supportFree, 0, - (size_t)event->ppwwwuuu.w2, - (size_t)event->ppwwwuuu.w3, - (size_t)event->ppwwwuuu.w4, - (mps_bool_t)event->ppwwwuuu.u5, - (mps_bool_t)event->ppwwwuuu.u6, - (mps_bool_t)event->ppwwwuuu.u7); - } break; - case EventPoolInitMV: { /* pool, arena, extendBy, avgSize, maxSize */ - /* .pool.control: The control pool will get created just after */ - /* its arena; ignore it. */ - if (!arenaJustCreated) { - poolRecreate(event->ppwww.p0, event->ppwww.p1, - mps_class_mv(), supportFree, 0, (size_t)event->ppwww.w2, - (size_t)event->ppwww.w3, (size_t)event->ppwww.w4); - } else { - arenaJustCreated = FALSE; - } - } break; - case EventPoolInitMFS: { /* pool, arena, extendBy, unitSize */ - /* internal only */ - ++discardedEvents; - } break; - case EventPoolInit: { /* pool, arena, class */ - /* all internal only */ - ++discardedEvents; - } break; - case EventPoolFinish: { /* pool */ - found = TableLookup(&entry, poolTable, (TableKey)event->p.p0); - if (found) { - poolRedestroy(event->p.p0); - } else { - ++discardedEvents; - } - } break; - case EventBufferInit: { /* buffer, pool, isMutator */ - if ((Bool)event->ppu.u2) { - found = TableLookup(&entry, poolTable, (TableKey)event->ppu.p1); - if (found) { - poolRep rep = (poolRep)entry; - - if(rep->bufferClassLevel == 0) { /* see .bufclass */ - apRecreate(event->ppu.p0, event->ppu.p1); - } else { - ++discardedEvents; - } - } else { - ++discardedEvents; - } - } else { - ++discardedEvents; - } - } break; - case EventBufferInitSeg: { /* buffer, pool, isMutator */ - if ((Bool)event->ppu.u2) { - found = TableLookup(&entry, poolTable, (TableKey)event->ppu.p1); - if (found) { - poolRep rep = (poolRep)entry; - - if(rep->bufferClassLevel == 1) { /* see .bufclass */ - apRecreate(event->ppu.p0, event->ppu.p1); - } else { - ++discardedEvents; - } - } else { - ++discardedEvents; - } - } else { - ++discardedEvents; - } - } break; - case EventBufferInitRank: { /* buffer, pool, isMutator, rank */ - if ((Bool)event->ppuu.u2) { - found = TableLookup(&entry, poolTable, (TableKey)event->ppuu.p1); - if (found) { - poolRep rep = (poolRep)entry; - - if(rep->bufferClassLevel == 2) { /* see .bufclass */ - apRecreate(event->ppuu.p0, event->ppuu.p1, event->ppuu.u3); - } else { - ++discardedEvents; - } - } else { - ++discardedEvents; - } - } else { - ++discardedEvents; - } - } break; - case EventBufferFinish: { /* buffer */ - found = TableLookup(&entry, apTable, (TableKey)event->p.p0); - if (found) { - apRedestroy(event->p.p0); - } else { - ++discardedEvents; - } - } break; - case EventBufferReserve: { /* buffer, init, size */ - found = TableLookup(&entry, apTable, (TableKey)event->paw.p0); - if (found) { - apRep rep = (apRep)entry; - mps_addr_t p; - - eres = mps_reserve(&p, rep->ap, (size_t)event->paw.w2); - verifyMPS(eres); - } else { - ++discardedEvents; - } - } break; - case EventBufferCommit: { /* buffer, p, size, clientClass */ - found = TableLookup(&entry, apTable, (TableKey)event->pawa.p0); - if (found) { - apRep rep = (apRep)entry; - mps_addr_t obj = rep->ap->init; - mps_bool_t committed; - size_t size = (size_t)event->pawa.w2; - - committed = mps_commit(rep->ap, obj, size); - verifyMPS(committed ? MPS_RES_OK : MPS_RES_FAIL); - objDefine(rep->objects, event->pawa.a1, obj, size); - } else { - ++discardedEvents; - } - } break; - case EventPoolAlloc: { /* pool, obj, size */ - found = TableLookup(&entry, poolTable, (TableKey)event->paw.p0); - if (found) { - poolRep rep = (poolRep)entry; - void *obj; - size_t size = (size_t)event->paw.w2; - - eres = mps_alloc(&obj, rep->pool, size); - verifyMPS(eres); - objDefine(rep->objects, event->paw.a1, obj, size); - } else { - ++discardedEvents; - } - } break; - case EventPoolFree: { /* pool, obj, size */ - found = TableLookup(&entry, poolTable, (TableKey)event->paw.p0); - if (found) { - poolRep rep = (poolRep)entry; - void *obj; - size_t size = (size_t)event->paw.w2; - - objRemove(&obj, rep->objects, event->paw.a1, size); - mps_free(rep->pool, obj, size); - } else { - ++discardedEvents; - } - } break; - case EventCommitLimitSet: { /* arena, limit, succeeded */ - found = TableLookup(&entry, arenaTable, (TableKey)event->pwu.p0); - verify(found); - eres = mps_arena_commit_limit_set((mps_arena_t)entry, - (size_t)event->pwu.w1); - verifyMPS(((Bool)event->pwu.u2 == (eres == MPS_RES_OK)) - ? MPS_RES_OK : MPS_RES_FAIL); - } break; - case EventSetSpare: { /* arena, spare */ - found = TableLookup(&entry, arenaTable, (TableKey)event->pd.p0); - verify(found); - mps_arena_spare_set((mps_arena_t)entry, event->pd.d1); - } break; - case EventReservoirLimitSet: { /* arena, limit */ - found = TableLookup(&entry, arenaTable, (TableKey)event->pw.p0); - verify(found); - mps_reservoir_limit_set((mps_arena_t)entry, (size_t)event->pw.w1); - } break; - case EventVMMap: case EventVMUnmap: - case EventVMInit: case EventVMFinish: - case EventArenaWriteFaults: - case EventArenaAlloc: case EventArenaAllocFail: case EventArenaFree: - case EventSegAlloc: case EventSegAllocFail: case EventSegFree: - case EventSegMerge: case EventSegSplit: - case EventBufferFill: case EventBufferEmpty: - case EventCBSInit: case EventMeterInit: case EventMeterValues: - case EventIntern: case EventLabel: { - ++discardedEvents; - } break; - default: { - ++unknownEvents; - if (unknownEvents < 12) /* don't output too much */ - printf("Unknown event @%ld: %s.\n", etime, - EventCode2Name(EventGetCode(event))); - } break; - } -} - - -/* Checking macros, copied from check.h */ - -#define COMPATLVALUE(lv1, lv2) \ - ((void)sizeof((lv1) = (lv2)), (void)sizeof((lv2) = (lv1)), TRUE) - -#define COMPATTYPE(t1, t2) \ - (sizeof(t1) == sizeof(t2) && \ - COMPATLVALUE(*((t1 *)0), *((t2 *)0))) - - -/* CHECKCONV -- check t2 can be cast to t1 without loss */ - -#define CHECKCONV(t1, t2) \ - (sizeof(t1) >= sizeof(t2)) - - -/* EventRepInit -- initialize the module */ - -Res EventRepInit(void) -{ - Res res; - - /* Check using pointers as keys in the tables. */ - verify(CHECKCONV(Word, void *)); - /* Check storage of MPS opaque handles in the tables. */ - verify(COMPATTYPE(mps_arena_t, void *)); - verify(COMPATTYPE(mps_ap_t, void *)); - /* .event-conv: Conversion of event fields into the types required */ - /* by the MPS functions is justified by the reverse conversion */ - /* being acceptable (which is upto the event log generator). */ - - totalEvents = 0; discardedEvents = 0; unknownEvents = 0; - - res = TableCreate(&arenaTable, (size_t)1); - if (res != ResOK) - goto failArena; - res = TableCreate(&poolTable, (size_t)1<<4); - if (res != ResOK) - goto failPool; - res = TableCreate(&apTable, (size_t)1<<6); - if (res != ResOK) - goto failAp; - - return ResOK; - -failAp: - TableDestroy(poolTable); -failPool: - TableDestroy(arenaTable); -failArena: - return res; -} - - -/* EventRepFinish -- finish the module */ - -void EventRepFinish(void) -{ - /* @@@@ add listing of remaining objects? */ - /* No point in cleaning up the tables, since we're quitting. */ - printf("Replayed %lu and discarded %lu events (%lu unknown).\n", - totalEvents - discardedEvents - unknownEvents, - discardedEvents + unknownEvents, unknownEvents); -} - - -/* 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/mps/code/failover.c b/mps/code/failover.c index 750c6b1b17b..86d1b193411 100644 --- a/mps/code/failover.c +++ b/mps/code/failover.c @@ -4,6 +4,11 @@ * Copyright (c) 2014 Ravenbrook Limited. See end of file for license. * * .design: + * + * .critical: In manual-allocation-bound programs using MVFF, many of + * these functions are on the critical paths via mps_alloc (and then + * PoolAlloc, MVFFAlloc, failoverFind*) and mps_free (and then + * MVFFFree, failoverInsert). */ #include "failover.h" @@ -13,9 +18,6 @@ SRCID(failover, "$Id$"); -#define failoverOfLand(land) PARENT(FailoverStruct, landStruct, land) - - ARG_DEFINE_KEY(failover_primary, Pointer); ARG_DEFINE_KEY(failover_secondary, Pointer); @@ -30,68 +32,54 @@ Bool FailoverCheck(Failover fo) } -static Res failoverInit(Land land, ArgList args) +static Res failoverInit(Land land, Arena arena, Align alignment, 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); + AVER(land != NULL); + res = NextMethod(Land, Failover, init)(land, arena, alignment, args); if (res != ResOK) return res; + fo = CouldBeA(Failover, land); ArgRequire(&arg, args, FailoverPrimary); - primary = arg.val.p; + fo->primary = arg.val.p; ArgRequire(&arg, args, FailoverSecondary); - secondary = arg.val.p; + fo->secondary = arg.val.p; - fo = failoverOfLand(land); - fo->primary = primary; - fo->secondary = secondary; + SetClassOfPoly(land, CLASS(Failover)); fo->sig = FailoverSig; - AVERT(Failover, fo); + AVERC(Failover, fo); + return ResOK; } -static void failoverFinish(Land land) +static void failoverFinish(Inst inst) { - Failover fo; - - AVERT(Land, land); - fo = failoverOfLand(land); - AVERT(Failover, fo); - + Land land = MustBeA(Land, inst); + Failover fo = MustBeA(Failover, land); fo->sig = SigInvalid; + NextMethod(Inst, Failover, finish)(inst); } static Size failoverSize(Land land) { - Failover fo; - - AVERT(Land, land); - fo = failoverOfLand(land); - AVERT(Failover, fo); - + Failover fo = MustBeA_CRITICAL(Failover, land); return LandSize(fo->primary) + LandSize(fo->secondary); } static Res failoverInsert(Range rangeReturn, Land land, Range range) { - Failover fo; + Failover fo = MustBeA_CRITICAL(Failover, land); Res res; - AVER(rangeReturn != NULL); - AVERT(Land, land); - fo = failoverOfLand(land); - AVERT(Failover, fo); - AVERT(Range, range); + AVER_CRITICAL(rangeReturn != NULL); + AVERT_CRITICAL(Range, range); /* Provide more opportunities for coalescence. See * . @@ -108,14 +96,11 @@ static Res failoverInsert(Range rangeReturn, Land land, Range range) static Res failoverDelete(Range rangeReturn, Land land, Range range) { - Failover fo; + Failover fo = MustBeA(Failover, land); 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 @@ -170,37 +155,31 @@ static Res failoverDelete(Range rangeReturn, Land land, Range range) } } if (res == ResOK) { - AVER(RangesNest(&oldRange, range)); + AVER_CRITICAL(RangesNest(&oldRange, range)); RangeCopy(rangeReturn, &oldRange); } return res; } -static Bool failoverIterate(Land land, LandVisitor visitor, void *closureP, Size closureS) +static Bool failoverIterate(Land land, LandVisitor visitor, void *closure) { - Failover fo; + Failover fo = MustBeA(Failover, land); - 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); + return LandIterate(fo->primary, visitor, closure) + && LandIterate(fo->secondary, visitor, closure); } static Bool failoverFindFirst(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) { - Failover fo; + Failover fo = MustBeA_CRITICAL(Failover, land); - AVER(rangeReturn != NULL); - AVER(oldRangeReturn != NULL); - AVERT(Land, land); - fo = failoverOfLand(land); - AVERT(Failover, fo); - AVERT(FindDelete, findDelete); + AVER_CRITICAL(rangeReturn != NULL); + AVER_CRITICAL(oldRangeReturn != NULL); + AVERT_CRITICAL(FindDelete, findDelete); /* See . */ (void)LandFlush(fo->primary, fo->secondary); @@ -212,14 +191,11 @@ static Bool failoverFindFirst(Range rangeReturn, Range oldRangeReturn, Land land static Bool failoverFindLast(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) { - Failover fo; + Failover fo = MustBeA_CRITICAL(Failover, land); - AVER(rangeReturn != NULL); - AVER(oldRangeReturn != NULL); - AVERT(Land, land); - fo = failoverOfLand(land); - AVERT(Failover, fo); - AVERT(FindDelete, findDelete); + AVER_CRITICAL(rangeReturn != NULL); + AVER_CRITICAL(oldRangeReturn != NULL); + AVERT_CRITICAL(FindDelete, findDelete); /* See . */ (void)LandFlush(fo->primary, fo->secondary); @@ -231,14 +207,11 @@ static Bool failoverFindLast(Range rangeReturn, Range oldRangeReturn, Land land, static Bool failoverFindLargest(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) { - Failover fo; + Failover fo = MustBeA_CRITICAL(Failover, land); - AVER(rangeReturn != NULL); - AVER(oldRangeReturn != NULL); - AVERT(Land, land); - fo = failoverOfLand(land); - AVERT(Failover, fo); - AVERT(FindDelete, findDelete); + AVER_CRITICAL(rangeReturn != NULL); + AVER_CRITICAL(oldRangeReturn != NULL); + AVERT_CRITICAL(FindDelete, findDelete); /* See . */ (void)LandFlush(fo->primary, fo->secondary); @@ -250,19 +223,16 @@ static Bool failoverFindLargest(Range rangeReturn, Range oldRangeReturn, Land la static Bool failoverFindInZones(Bool *foundReturn, Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high) { - Failover fo; + Failover fo = MustBeA_CRITICAL(Failover, land); 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); + AVER_CRITICAL(FALSE); /* TODO: this code is completely untested! */ + AVER_CRITICAL(foundReturn != NULL); + AVER_CRITICAL(rangeReturn != NULL); + AVER_CRITICAL(oldRangeReturn != NULL); + /* AVERT_CRITICAL(ZoneSet, zoneSet); */ + AVERT_CRITICAL(Bool, high); /* See . */ (void)LandFlush(fo->primary, fo->secondary); @@ -276,48 +246,52 @@ static Bool failoverFindInZones(Bool *foundReturn, Range rangeReturn, Range oldR } -static Res failoverDescribe(Land land, mps_lib_FILE *stream, Count depth) +static Res failoverDescribe(Inst inst, mps_lib_FILE *stream, Count depth) { - Failover fo; + Land land = CouldBeA(Land, inst); + Failover fo = CouldBeA(Failover, land); + LandClass primaryClass, secondaryClass; Res res; - if (!TESTT(Land, land)) - return ResFAIL; - fo = failoverOfLand(land); - if (!TESTT(Failover, fo)) - return ResFAIL; + if (!TESTC(Failover, fo)) + return ResPARAM; if (stream == NULL) - return ResFAIL; + return ResPARAM; - 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); + res = NextMethod(Inst, Failover, describe)(inst, stream, depth); + if (res != ResOK) + return res; - return res; + primaryClass = ClassOfPoly(Land, fo->primary); + secondaryClass = ClassOfPoly(Land, fo->secondary); + + return WriteF(stream, depth + 2, + "primary = $P ($S)\n", + (WriteFP)fo->primary, + (WriteFS)ClassName(primaryClass), + "secondary = $P ($S)\n", + (WriteFP)fo->secondary, + (WriteFS)ClassName(secondaryClass), + NULL); } -DEFINE_LAND_CLASS(FailoverLandClass, class) +DEFINE_CLASS(Land, Failover, klass) { - 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); + INHERIT_CLASS(klass, Failover, Land); + klass->instClassStruct.describe = failoverDescribe; + klass->instClassStruct.finish = failoverFinish; + klass->size = sizeof(FailoverStruct); + klass->init = failoverInit; + klass->sizeMethod = failoverSize; + klass->insert = failoverInsert; + klass->delete = failoverDelete; + klass->iterate = failoverIterate; + klass->findFirst = failoverFindFirst; + klass->findLast = failoverFindLast; + klass->findLargest = failoverFindLargest; + klass->findInZones = failoverFindInZones; + AVERT(LandClass, klass); } diff --git a/mps/code/failover.h b/mps/code/failover.h index 3676bade103..a74ab69a1fb 100644 --- a/mps/code/failover.h +++ b/mps/code/failover.h @@ -10,6 +10,8 @@ #define failover_h #include "mpmtypes.h" +#include "mpm.h" +#include "protocol.h" typedef struct FailoverStruct *Failover; @@ -17,7 +19,7 @@ typedef struct FailoverStruct *Failover; extern Bool FailoverCheck(Failover failover); -extern LandClass FailoverLandClassGet(void); +DECLARE_CLASS(Land, Failover, Land); extern const struct mps_key_s _mps_key_failover_primary; #define FailoverPrimary (&_mps_key_failover_primary) diff --git a/mps/code/fbmtest.c b/mps/code/fbmtest.c deleted file mode 100644 index f5aa6831db8..00000000000 --- a/mps/code/fbmtest.c +++ /dev/null @@ -1,663 +0,0 @@ -/* fbmtest.c: FREE BLOCK MANAGEMENT TEST - * - * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. - * - * The MPS contains two free block management modules: - * - * 1. the CBS (Coalescing Block Structure) module maintains free - * blocks in a splay tree for fast access with a cost in storage; - * - * 2. the Freelist module maintains free blocks in an address-ordered - * singly linked list for zero storage overhead with a cost in - * performance. - * - * The two modules present identical interfaces, so we apply the same - * test cases to both. - */ - -#include "cbs.h" -#include "freelist.h" -#include "mpm.h" -#include "mps.h" -#include "mpsavm.h" -#include "testlib.h" - -#include /* printf */ - -SRCID(fbmtest, "$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) - -static Count NAllocateTried, NAllocateSucceeded, NDeallocateTried, - NDeallocateSucceeded; - -static Bool verbose = FALSE; - -typedef unsigned FBMType; -enum { - FBMTypeCBS = 1, - FBMTypeFreelist, - FBMTypeLimit -}; - -typedef struct FBMStateStruct { - FBMType type; - Align align; - BT allocTable; - Addr block; - union { - CBS cbs; - Freelist fl; - } the; -} FBMStateStruct, *FBMState; - -typedef struct CheckFBMClosureStruct { - FBMState state; - Addr limit; - Addr oldLimit; -} CheckFBMClosureStruct, *CheckFBMClosure; - - -static Addr (addrOfIndex)(FBMState state, Index i) -{ - return AddrAdd(state->block, (i * state->align)); -} - - -static Index (indexOfAddr)(FBMState state, Addr a) -{ - return (Index)(AddrOffset(state->block, a) / state->align); -} - - -static void describe(FBMState state) { - switch (state->type) { - case FBMTypeCBS: - die(CBSDescribe(state->the.cbs, mps_lib_get_stdout(), 0), - "CBSDescribe"); - break; - case FBMTypeFreelist: - die(FreelistDescribe(state->the.fl, mps_lib_get_stdout(), 0), - "FreelistDescribe"); - break; - default: - cdie(0, "invalid state->type"); - break; - } -} - - -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); - - 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 Bool checkCBSCallback(CBS cbs, Range range, - void *closureP, Size closureS) -{ - UNUSED(cbs); - return checkCallback(range, closureP, closureS); -} - - -static Bool checkFLCallback(Bool *deleteReturn, Range range, - void *closureP, Size closureS) -{ - *deleteReturn = FALSE; - return checkCallback(range, closureP, closureS); -} - - -static void check(FBMState state) -{ - CheckFBMClosureStruct closure; - - closure.state = state; - closure.limit = addrOfIndex(state, ArraySize); - closure.oldLimit = state->block; - - switch (state->type) { - case FBMTypeCBS: - CBSIterate(state->the.cbs, checkCBSCallback, &closure, UNUSED_SIZE); - break; - case FBMTypeFreelist: - FreelistIterate(state->the.fl, checkFLCallback, &closure, UNUSED_SIZE); - break; - default: - cdie(0, "invalid state->type"); - return; - } - - 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, FBMState 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(ArraySize); - - do { - end = nextEdge(state->allocTable, ArraySize, base); - } while(end < ArraySize && 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(FBMState 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) { - Size left, right, total; /* Sizes of block and two fragments */ - - outerBase = - addrOfIndex(state, lastEdge(state->allocTable, ArraySize, ib)); - outerLimit = - addrOfIndex(state, nextEdge(state->allocTable, ArraySize, il - 1)); - - left = AddrOffset(outerBase, base); - right = AddrOffset(limit, outerLimit); - total = AddrOffset(outerBase, outerLimit); - - /* TODO: check these values */ - UNUSED(left); - UNUSED(right); - UNUSED(total); - } else { - outerBase = outerLimit = NULL; - } - - RangeInit(&range, base, limit); - switch (state->type) { - case FBMTypeCBS: - res = CBSDelete(&oldRange, state->the.cbs, &range); - break; - case FBMTypeFreelist: - res = FreelistDelete(&oldRange, state->the.fl, &range); - break; - default: - cdie(0, "invalid state->type"); - return; - } - - 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(FBMState 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) { - Size left, right, total; /* Sizes of block and two fragments */ - - /* Find the free blocks adjacent to the allocated block */ - if (ib > 0 && !BTGet(state->allocTable, ib - 1)) { - outerBase = - addrOfIndex(state, lastEdge(state->allocTable, ArraySize, ib - 1)); - } else { - outerBase = base; - } - - if (il < ArraySize && !BTGet(state->allocTable, il)) { - outerLimit = - addrOfIndex(state, nextEdge(state->allocTable, ArraySize, il)); - } else { - outerLimit = limit; - } - - left = AddrOffset(outerBase, base); - right = AddrOffset(limit, outerLimit); - total = AddrOffset(outerBase, outerLimit); - - /* TODO: check these values */ - UNUSED(left); - UNUSED(right); - UNUSED(total); - } - - RangeInit(&range, base, limit); - switch (state->type) { - case FBMTypeCBS: - res = CBSInsert(&freeRange, state->the.cbs, &range); - break; - case FBMTypeFreelist: - res = FreelistInsert(&freeRange, state->the.fl, &range); - break; - default: - cdie(0, "invalid state->type"); - return; - } - - 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(FBMState state, Size size, Bool high, FindDelete findDelete) -{ - Bool expected, found; - Index expectedBase, expectedLimit; - RangeStruct foundRange, oldRange; - Addr remainderBase, remainderLimit; - Addr origBase, origLimit; - Size oldSize, newSize; - - origBase = origLimit = NULL; - expected = (high ? BTFindLongResRangeHigh : BTFindLongResRange) - (&expectedBase, &expectedLimit, state->allocTable, - (Index)0, (Index)ArraySize, (Count)size); - - if (expected) { - oldSize = (expectedLimit - expectedBase) * state->align; - remainderBase = origBase = addrOfIndex(state, expectedBase); - remainderLimit = origLimit = addrOfIndex(state, expectedLimit); - - switch(findDelete) { - case FindDeleteNONE: - /* do nothing */ - break; - case FindDeleteENTIRE: - remainderBase = remainderLimit; - break; - case FindDeleteLOW: - expectedLimit = expectedBase + size; - remainderBase = addrOfIndex(state, expectedLimit); - break; - case FindDeleteHIGH: - expectedBase = expectedLimit - size; - remainderLimit = addrOfIndex(state, expectedBase); - break; - default: - cdie(0, "invalid findDelete"); - break; - } - - if (findDelete != FindDeleteNONE) { - newSize = AddrOffset(remainderBase, remainderLimit); - } - - /* TODO: check these values */ - UNUSED(oldSize); - UNUSED(newSize); - } - - switch (state->type) { - case FBMTypeCBS: - found = (high ? CBSFindLast : CBSFindFirst) - (&foundRange, &oldRange, state->the.cbs, size * state->align, findDelete); - break; - case FBMTypeFreelist: - found = (high ? FreelistFindLast : FreelistFindFirst) - (&foundRange, &oldRange, state->the.fl, size * state->align, findDelete); - break; - default: - cdie(0, "invalid state->type"); - return; - } - - 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(FBMState state, unsigned n) { - Addr base, limit; - unsigned i; - Size size; - Bool high; - FindDelete findDelete = FindDeleteNONE; - - BTSetRange(state->allocTable, 0, ArraySize); /* 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(ArraySize / 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 state->type"); - return; - } - if ((i + 1) % 1000 == 0) - check(state); - if (i == 100) - describe(state); - } -} - -#define testArenaSIZE (((size_t)4)<<20) - -extern int main(int argc, char *argv[]) -{ - mps_arena_t mpsArena; - Arena arena; /* the ANSI arena which we use to allocate the BT */ - FBMStateStruct state; - void *p; - Addr dummyBlock; - BT allocTable; - FreelistStruct flStruct; - CBSStruct cbsStruct; - Align align; - - testlib_init(argc, argv); - align = sizeof(void *) << (rnd() % 4); - - 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(&allocTable, arena, ArraySize), - "failed to create alloc table"); - - /* We're not going to use this block, but I feel unhappy just */ - /* inventing addresses. */ - die((mps_res_t)ControlAlloc(&p, arena, ArraySize * align, - /* withReservoirPermit */ FALSE), - "failed to allocate block"); - dummyBlock = p; /* avoid pun */ - - if (verbose) { - printf("Allocated block [%p,%p)\n", (void*)dummyBlock, - (char *)dummyBlock + ArraySize); - } - - 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; - state.block = dummyBlock; - state.allocTable = allocTable; - state.the.cbs = &cbsStruct; - test(&state, nCBSOperations); - CBSFinish(&cbsStruct); - - die((mps_res_t)FreelistInit(&flStruct, align), - "failed to initialise Freelist"); - state.type = FBMTypeFreelist; - state.the.fl = &flStruct; - test(&state, nFLOperations); - FreelistFinish(&flStruct); - - 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/mps/code/finalcv.c b/mps/code/finalcv.c index 2be7f083735..1871141bbdf 100644 --- a/mps/code/finalcv.c +++ b/mps/code/finalcv.c @@ -1,7 +1,7 @@ /* finalcv.c: FINALIZATION COVERAGE TEST * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. * * DESIGN @@ -40,7 +40,7 @@ #define finalizationRATE 6 #define gcINTERVAL ((size_t)150 * 1024) #define collectionCOUNT 3 -#define messageCOUNT 3 +#define finalizationCOUNT 3 /* 3 words: wrapper | vector-len | first-slot */ #define vectorSIZE (3*sizeof(mps_word_t)) @@ -110,11 +110,11 @@ static void test(mps_arena_t arena, mps_pool_class_t pool_class) mps_root_t mps_root[2]; mps_addr_t nullref = NULL; int state[rootCOUNT]; - mps_message_t message; - size_t messages = 0; + size_t finalizations = 0; + size_t collections = 0; void *p; - printf("---- finalcv: pool class %s ----\n", pool_class->name); + printf("---- finalcv: pool class %s ----\n", ClassName(pool_class)); die(mps_fmt_create_A(&fmt, arena, dylan_fmt_A()), "fmt_create\n"); die(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); @@ -143,15 +143,21 @@ static void test(mps_arena_t arena, mps_pool_class_t pool_class) /* store index in vector's slot */ ((mps_word_t *)p)[vectorSLOT] = dylan_int(i); + /* mps_definalize fails when there have been no calls to mps_finalize + yet, or for an address that was not registered for finalization. */ + Insist(mps_definalize(arena, &p) == MPS_RES_FAIL); + die(mps_finalize(arena, &p), "finalize\n"); root[i] = p; state[i] = rootSTATE; } p = NULL; mps_message_type_enable(arena, mps_message_type_finalization()); + mps_message_type_enable(arena, mps_message_type_gc()); /* */ - while (messages < messageCOUNT && mps_collections(arena) < collectionCOUNT) { + while (finalizations < finalizationCOUNT && collections < collectionCOUNT) { + mps_message_type_t type; /* Perhaps cause (minor) collection */ churn(ap); @@ -177,31 +183,37 @@ static void test(mps_arena_t arena, mps_pool_class_t pool_class) } } - /* Test any finalized objects, and perhaps resurrect some */ - while (mps_message_poll(arena)) { - mps_word_t *obj; - mps_word_t objind; - mps_addr_t objaddr; + while (mps_message_queue_type(&type, arena)) { + mps_message_t message; + cdie(mps_message_get(&message, arena, type), "message_get"); + if (type == mps_message_type_finalization()) { + /* Check finalized object, and perhaps resurrect it. */ + mps_word_t *obj; + mps_word_t objind; + mps_addr_t objaddr; - /* */ - cdie(mps_message_get(&message, arena, mps_message_type_finalization()), - "get"); - cdie(0 == mps_message_clock(arena, message), - "message clock should be 0 (unset) for finalization messages"); - mps_message_finalization_ref(&objaddr, arena, message); - obj = objaddr; - objind = dylan_int_int(obj[vectorSLOT]); - printf("Finalizing: object %"PRIuLONGEST" at %p\n", - (ulongest_t)objind, objaddr); - /* */ - cdie(root[objind] == NULL, "finalized live"); - cdie(state[objind] == finalizableSTATE, "finalized dead"); - state[objind] = finalizedSTATE; - /* sometimes resurrect */ - if (rnd() % 2 == 0) - root[objind] = objaddr; + /* */ + cdie(0 == mps_message_clock(arena, message), + "message clock should be 0 (unset) for finalization messages"); + mps_message_finalization_ref(&objaddr, arena, message); + obj = objaddr; + objind = dylan_int_int(obj[vectorSLOT]); + printf("Finalizing: object %"PRIuLONGEST" at %p\n", + (ulongest_t)objind, objaddr); + /* */ + cdie(root[objind] == NULL, "finalized live"); + cdie(state[objind] == finalizableSTATE, "finalized dead"); + state[objind] = finalizedSTATE; + /* sometimes resurrect */ + if (rnd() % 2 == 0) + root[objind] = objaddr; + ++ finalizations; + } else if (type == mps_message_type_gc()) { + ++ collections; + } else { + error("Unexpected message type %lu.", (unsigned long)type); + } mps_message_discard(arena, message); - ++ messages; } } @@ -238,7 +250,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2014 Ravenbrook Limited . + * Copyright (c) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/finaltest.c b/mps/code/finaltest.c index 9f3ef13b967..877e37f9699 100644 --- a/mps/code/finaltest.c +++ b/mps/code/finaltest.c @@ -43,6 +43,7 @@ #include "fmtdytst.h" #include "mpstd.h" +#include /* HUGE_VAL */ #include /* fflush, printf, stdout */ enum { @@ -149,13 +150,15 @@ static void test_trees(int mode, const char *name, mps_arena_t arena, size_t finals = 0; size_t i; int object_alloc; + PoolClass klass = ClassOfPoly(Pool, pool); object_count = 0; printf("---- Mode %s, pool class %s, %s trees ----\n", mode == ModePARK ? "PARK" : "POLL", - pool->class->name, name); + ClassName(klass), name); mps_arena_park(arena); + mps_message_type_enable(arena, mps_message_type_gc()); /* make some trees */ for(i = 0; i < rootCOUNT; ++i) { @@ -169,6 +172,7 @@ static void test_trees(int mode, const char *name, mps_arena_t arena, } while (finals < object_count && collections < collectionCOUNT) { + mps_message_type_t type; mps_word_t final_this_time = 0; switch (mode) { default: @@ -188,30 +192,43 @@ static void test_trees(int mode, const char *name, mps_arena_t arena, 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); + size_t total_size = mps_pool_total_size(pool); + size_t free_size = mps_pool_free_size(pool); + Insist(free_size <= total_size); + Insist(free_size + live_size <= total_size); } - while (mps_message_poll(arena)) { + while (mps_message_queue_type(&type, 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); + cdie(mps_message_get(&message, arena, type), "message_get"); + if (type == mps_message_type_finalization()) { + mps_addr_t objaddr; + mps_message_finalization_ref(&objaddr, arena, message); + ++ final_this_time; + } else if (type == mps_message_type_gc()) { + ++ collections; + } else { + error("Unexpected message type %lu.", (unsigned long)type); + } 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) + + if (finals != object_count) { + PoolClass poolClass = ClassOfPoly(Pool, BufferOfAP(ap)->pool); error("Not all objects were finalized for %s in mode %s.", - BufferOfAP(ap)->pool->class->name, + ClassName(poolClass), mode == ModePOLL ? "POLL" : "PARK"); + } + + if (collections > collectionCOUNT) + error("Expected no more than %lu collections but got %lu.", + (unsigned long)collectionCOUNT, (unsigned long)collections); } static void test_pool(int mode, mps_arena_t arena, mps_chain_t chain, @@ -270,8 +287,18 @@ int main(int argc, char *argv[]) testlib_init(argc, argv); - die(mps_arena_create(&arena, mps_arena_class_vm(), testArenaSIZE), - "arena_create\n"); + MPS_ARGS_BEGIN(args) { + /* Randomize pause time as a regression test for job004007. */ + double t = rnd_double(); + if (t == 0.0) + t = HUGE_VAL; /* Would prefer to use INFINITY but it's not in C89. */ + else + t = 1 / t - 1; + MPS_ARGS_ADD(args, MPS_KEY_PAUSE_TIME, t); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, testArenaSIZE); + die(mps_arena_create_k(&arena, mps_arena_class_vm(), args), + "arena_create\n"); + } MPS_ARGS_END(args); mps_message_type_enable(arena, mps_message_type_finalization()); die(mps_thread_reg(&thread, arena), "thread_reg\n"); for (i = 0; i < gens; ++i) { diff --git a/mps/code/fmtdy.c b/mps/code/fmtdy.c index 0cb4adea11d..2f88fd53d82 100644 --- a/mps/code/fmtdy.c +++ b/mps/code/fmtdy.c @@ -1,7 +1,7 @@ /* fmtdy.c: DYLAN OBJECT FORMAT IMPLEMENTATION * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * Portions copyright (c) 2002 Global Graphics Software. * * .readership: MPS developers, Dylan developers @@ -96,7 +96,7 @@ int dylan_wrapper_check(mps_word_t *w) mps_word_t vh; mps_word_t version; mps_word_t reserved; - mps_word_t class; + mps_word_t klass; mps_word_t fh, fl, ff; mps_word_t vb, es, vf; mps_word_t vt, t; @@ -129,8 +129,8 @@ int dylan_wrapper_check(mps_word_t *w) /* Unpack the wrapper. */ - class = w[WC]; /* class */ - unused(class); + klass = w[WC]; /* class */ + unused(klass); fh = w[WF]; /* fixed part header word */ fl = fh >> 2; /* fixed part length */ ff = fh & 3; /* fixed part format code */ @@ -152,8 +152,8 @@ int dylan_wrapper_check(mps_word_t *w) /* The second word is the class of the wrapped object. */ /* It would be good to check which pool this is in. */ - assert(class != 0); /* class exists */ - assert((class & 3) == 0); /* class is aligned */ + assert(klass != 0); /* class exists */ + assert((klass & 3) == 0); /* class is aligned */ /* The third word contains the fixed part format and length. */ /* The only illegal format is 3. Anything else is possible, although */ @@ -236,7 +236,7 @@ static mps_res_t dylan_scan_contig(mps_ss_t mps_ss, /* dylan_weak_dependent -- returns the linked object, if any. */ -extern mps_addr_t dylan_weak_dependent(mps_addr_t parent) +mps_addr_t dylan_weak_dependent(mps_addr_t parent) { mps_word_t *object; mps_word_t *wrapper; @@ -366,7 +366,7 @@ static mps_res_t dylan_scan_pat(mps_ss_t mps_ss, (_vt) << ((_es) - FMTDY_WORD_SHIFT)) -extern mps_res_t dylan_scan1(mps_ss_t mps_ss, mps_addr_t *object_io) +mps_res_t dylan_scan1(mps_ss_t mps_ss, mps_addr_t *object_io) { mps_addr_t *p; /* cursor in object */ mps_addr_t *q; /* cursor limit for loops */ @@ -407,8 +407,11 @@ extern mps_res_t dylan_scan1(mps_ss_t mps_ss, mps_addr_t *object_io) return MPS_RES_OK; } - res = mps_fix(mps_ss, p); /* fix the wrapper */ - if ( res != MPS_RES_OK ) return res; + MPS_SCAN_BEGIN(mps_ss) { + res = MPS_FIX12(mps_ss, p); /* fix the wrapper */ + } MPS_SCAN_END(mps_ss); + if (res != MPS_RES_OK) + return res; w = (mps_word_t *)p[0]; /* wrapper is header word */ assert(dylan_wrapper_check(w)); @@ -546,7 +549,7 @@ static mps_addr_t dylan_class(mps_addr_t obj) return (mps_addr_t)first_word; } -extern mps_res_t dylan_scan1_weak(mps_ss_t mps_ss, mps_addr_t *object_io) +mps_res_t dylan_scan1_weak(mps_ss_t mps_ss, mps_addr_t *object_io) { mps_addr_t *assoc; mps_addr_t *base; @@ -567,8 +570,11 @@ extern mps_res_t dylan_scan1_weak(mps_ss_t mps_ss, mps_addr_t *object_io) assert((h & 3) == 0); unused(h); - res = mps_fix(mps_ss, p); - if ( res != MPS_RES_OK ) return res; + MPS_SCAN_BEGIN(mps_ss) { + res = MPS_FIX12(mps_ss, p); + } MPS_SCAN_END(mps_ss); + if (res != MPS_RES_OK) + return res; /* w points to wrapper */ w = (mps_word_t *)p[0]; @@ -628,7 +634,7 @@ static mps_res_t dylan_scan_weak(mps_ss_t mps_ss, return MPS_RES_OK; } -static mps_addr_t dylan_skip(mps_addr_t object) +mps_addr_t dylan_skip(mps_addr_t object) { mps_addr_t *p; /* cursor in object */ mps_word_t *w; /* wrapper cursor */ @@ -746,6 +752,14 @@ void dylan_pad(mps_addr_t addr, size_t size) } } +mps_bool_t dylan_ispad(mps_addr_t addr) +{ + mps_word_t *p; + + p = (mps_word_t *)addr; + return p[0] == 1 || p[0] == 2; +} + /* The dylan format structures */ @@ -844,7 +858,7 @@ mps_res_t dylan_fmt_weak(mps_fmt_t *mps_fmt_o, mps_arena_t arena) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/fmtdy.h b/mps/code/fmtdy.h index b7434abebd7..67483639c2b 100644 --- a/mps/code/fmtdy.h +++ b/mps/code/fmtdy.h @@ -24,8 +24,10 @@ extern mps_res_t dylan_fmt_weak(mps_fmt_t *, mps_arena_t); extern mps_addr_t dylan_weak_dependent(mps_addr_t); -extern void dylan_pad(mps_addr_t addr, size_t size); -extern int dylan_wrapper_check(mps_word_t *w); +extern mps_addr_t dylan_skip(mps_addr_t); +extern void dylan_pad(mps_addr_t, size_t); +extern mps_bool_t dylan_ispad(mps_addr_t); +extern int dylan_wrapper_check(mps_word_t *); /* Constants describing wrappers. Used only for debugging / testing */ #define WW 0 /* offset of Wrapper-Wrapper */ diff --git a/mps/code/fmtscheme.c b/mps/code/fmtscheme.c index 24fa06db871..979672c9de3 100644 --- a/mps/code/fmtscheme.c +++ b/mps/code/fmtscheme.c @@ -1,7 +1,7 @@ /* 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. + * $Id$ + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. */ #include @@ -460,7 +460,7 @@ void scheme_fmt(mps_fmt_t *fmt) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/fmtscheme.h b/mps/code/fmtscheme.h index 1149d9496a1..92d4c270bb6 100644 --- a/mps/code/fmtscheme.h +++ b/mps/code/fmtscheme.h @@ -1,7 +1,7 @@ /* 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. + * $Id$ + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. */ #ifndef fmtscheme_h @@ -193,7 +193,7 @@ extern mps_ap_t obj_ap; /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/forktest.c b/mps/code/forktest.c new file mode 100644 index 00000000000..e1d1909d4c1 --- /dev/null +++ b/mps/code/forktest.c @@ -0,0 +1,231 @@ +/* forktest.c: FORK SAFETY TEST + * + * $Id: //info.ravenbrook.com/project/mps/branch/2018-06-13/fork/code/tagtest.c#1 $ + * Copyright (c) 2018 Ravenbrook Limited. See end of file for license. + * + * .overview: This test case is a regression test for job004062. It + * checks that the MPS correctly runs in the child process after a + * fork() on FreeBSD, Linux or macOS. + * + * .format: This test case uses a trivial object format in which each + * object contains a single reference. + */ + +#include +#include +#include + +#include "mps.h" +#include "mpsavm.h" +#include "mpscamc.h" +#include "testlib.h" + +enum { + TYPE_REF, + TYPE_FWD, + TYPE_PAD +}; + +typedef struct obj_s { + unsigned type; /* One of the TYPE_ enums */ + union { + struct obj_s *ref; /* TYPE_REF */ + mps_addr_t fwd; /* TYPE_FWD */ + size_t pad; /* TYPE_PAD */ + } u; +} obj_s, *obj_t; + +static void obj_fwd(mps_addr_t old, mps_addr_t new) +{ + obj_t obj = old; + obj->type = TYPE_FWD; + obj->u.fwd = new; +} + +static mps_addr_t obj_isfwd(mps_addr_t addr) +{ + obj_t obj = addr; + if (obj->type == TYPE_FWD) { + return obj->u.fwd; + } else { + return NULL; + } +} + +static void obj_pad(mps_addr_t addr, size_t size) +{ + obj_t obj = addr; + obj->type = TYPE_PAD; + obj->u.pad = size; +} + +static mps_addr_t obj_skip(mps_addr_t addr) +{ + obj_t obj = addr; + size_t size; + if (obj->type == TYPE_PAD) { + size = obj->u.pad; + } else { + size = sizeof(obj_s); + } + return (char *)addr + size; +} + +static mps_res_t obj_scan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) +{ + MPS_SCAN_BEGIN(ss) { + while (base < limit) { + obj_t obj = base; + if (obj->type == TYPE_REF) { + mps_addr_t p = obj->u.ref; + mps_res_t res = MPS_FIX12(ss, &p); + if (res != MPS_RES_OK) { + return res; + } + obj->u.ref = p; + } + base = obj_skip(base); + } + } MPS_SCAN_END(ss); + return MPS_RES_OK; +} + +int main(int argc, char *argv[]) +{ + void *marker = ▮ + int pid; + mps_arena_t arena; + mps_fmt_t obj_fmt; + mps_pool_t pool; + mps_thr_t thread; + mps_root_t stack_root; + mps_ap_t obj_ap; + size_t i; + obj_t obj, first; + + testlib_init(argc, argv); + + /* Set the pause time to be very small so that the incremental + collector (when it runs) will have to leave a read barrier in + place for us to hit. */ + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_PAUSE_TIME, 0.0); + die(mps_arena_create_k(&arena, mps_arena_class_vm(), args), + "mps_arena_create"); + } MPS_ARGS_END(args); + mps_arena_park(arena); + + die(mps_thread_reg(&thread, arena), "Couldn't register thread"); + die(mps_root_create_thread(&stack_root, arena, thread, marker), + "Couldn't create thread root"); + + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_FMT_ALIGN, sizeof(obj_s)); + 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); + die(mps_fmt_create_k(&obj_fmt, arena, args), "Couldn't create obj format"); + } MPS_ARGS_END(args); + + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_FORMAT, obj_fmt); + die(mps_pool_create_k(&pool, arena, mps_class_amc(), args), + "Couldn't create pool"); + } MPS_ARGS_END(args); + + die(mps_ap_create_k(&obj_ap, pool, mps_args_none), + "Couldn't create obj allocation point"); + + /* Create a linked list of objects. */ + first = NULL; + for (i = 0; i < 100000; ++i) { + size_t size = sizeof(obj_s); + mps_addr_t addr; + do { + die(mps_reserve(&addr, obj_ap, size), "Couldn't allocate."); + obj = addr; + obj->type = TYPE_REF; + obj->u.ref = NULL; + } while (!mps_commit(obj_ap, addr, size)); + obj->u.ref = first; + first = obj; + } + + pid = fork(); + cdie(pid >= 0, "fork failed"); + + /* Allow a collection to start, which will cause a read barrier to + be applied to any segment containing live objects that was + scanned. */ + mps_arena_release(arena); + + /* Read all the objects, so that if there is read barrier in place + we will hit it. */ + for (obj = first; obj != NULL; obj = obj->u.ref) { + Insist(obj->type == TYPE_REF); + } + + mps_arena_park(arena); + + if (pid != 0) { + /* Parent: wait for child and check that its exit status is zero. */ + int stat; + cdie(pid == waitpid(pid, &stat, 0), "waitpid failed"); + cdie(WIFEXITED(stat), "child did not exit normally"); + cdie(WEXITSTATUS(stat) == 0, "child exited with nonzero status"); + printf("%s: Conclusion: Failed to find any defects.\n", argv[0]); + } + + mps_ap_destroy(obj_ap); + mps_pool_destroy(pool); + mps_fmt_destroy(obj_fmt); + mps_root_destroy(stack_root); + mps_thread_dereg(thread); + mps_arena_destroy(arena); + + return 0; +} + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (c) 2018 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/mps/code/format.c b/mps/code/format.c index 26b3e4bb751..b9c4a59fb14 100644 --- a/mps/code/format.c +++ b/mps/code/format.c @@ -1,7 +1,7 @@ /* format.c: OBJECT FORMATS * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. * Portions copyright (c) 2002 Global Graphics Software. * * DESIGN @@ -32,7 +32,7 @@ Bool FormatCheck(Format format) CHECKL(FUNCHECK(format->move)); CHECKL(FUNCHECK(format->isMoved)); CHECKL(FUNCHECK(format->pad)); - CHECKL(FUNCHECK(format->class)); + CHECKL(FUNCHECK(format->klass)); return TRUE; } @@ -133,14 +133,14 @@ Res FormatCreate(Format *formatReturn, Arena arena, ArgList args) if (ArgPick(&arg, args, MPS_KEY_FMT_CLASS)) fmtClass = arg.val.fmt_class; - res = ControlAlloc(&p, arena, sizeof(FormatStruct), - /* withReservoirPermit */ FALSE); + res = ControlAlloc(&p, arena, sizeof(FormatStruct)); if(res != ResOK) return res; format = (Format)p; /* avoid pun */ format->arena = arena; RingInit(&format->arenaRing); + format->poolCount = 0; format->alignment = fmtAlign; format->headerSize = fmtHeaderSize; format->scan = fmtScan; @@ -148,7 +148,7 @@ Res FormatCreate(Format *formatReturn, Arena arena, ArgList args) format->move = fmtFwd; format->isMoved = fmtIsfwd; format->pad = fmtPad; - format->class = fmtClass; + format->klass = fmtClass; format->sig = FormatSig; format->serial = arena->formatSerial; @@ -168,6 +168,7 @@ Res FormatCreate(Format *formatReturn, Arena arena, ArgList args) void FormatDestroy(Format format) { AVERT(Format, format); + AVER(format->poolCount == 0); /* */ RingRemove(&format->arenaRing); @@ -230,6 +231,7 @@ Res FormatDescribe(Format format, mps_lib_FILE *stream, Count depth) "Format $P ($U) {\n", (WriteFP)format, (WriteFU)format->serial, " arena $P ($U)\n", (WriteFP)format->arena, (WriteFU)format->arena->serial, + " poolCount $U\n", (WriteFU)format->poolCount, " alignment $W\n", (WriteFW)format->alignment, " scan $F\n", (WriteFF)format->scan, " skip $F\n", (WriteFF)format->skip, @@ -248,7 +250,7 @@ Res FormatDescribe(Format format, mps_lib_FILE *stream, Count depth) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/fotest.c b/mps/code/fotest.c index 750883f61a4..262414bacf7 100644 --- a/mps/code/fotest.c +++ b/mps/code/fotest.c @@ -1,7 +1,7 @@ /* fotest.c: FAIL-OVER TEST * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 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 @@ -10,9 +10,8 @@ * request due to running out of memory, they fall back to a Freelist * (which has zero memory overhead, at some cost in performance). * - * This is a white box test: it patches the class of the CBS's - * internal block pool (MFS) with a pointer to a dummy class whose - * alloc() method always returns ResMEMORY. + * This is a white box test: it monkey-patches the MFS pool's alloc + * method with a method that always returns a memory error code. */ @@ -36,43 +35,6 @@ #define testLOOPS 10 -/* Accessors for the CBS used to implement a pool. */ - -extern Land _mps_mvff_cbs(Pool); -extern Land _mps_mvt_cbs(Pool); - - -/* "OOM" pool class -- dummy alloc/free pool class whose alloc() - * method always fails and whose free method does nothing. */ - -static Res oomAlloc(Addr *pReturn, Pool pool, Size size, - Bool withReservoirPermit) -{ - UNUSED(pReturn); - UNUSED(pool); - UNUSED(size); - UNUSED(withReservoirPermit); - switch (rnd() % 3) { - case 0: - return ResRESOURCE; - case 1: - return ResMEMORY; - default: - return ResCOMMIT_LIMIT; - } -} - -extern PoolClass OOMPoolClassGet(void); -DEFINE_POOL_CLASS(OOMPoolClass, this) -{ - INHERIT_CLASS(this, AbstractPoolClass); - this->alloc = oomAlloc; - this->free = PoolTrivFree; - this->size = sizeof(PoolStruct); - AVERT(PoolClass, this); -} - - /* make -- allocate one object */ static mps_res_t make(mps_addr_t *p, mps_ap_t ap, size_t size) @@ -89,19 +51,44 @@ 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. */ +/* The original alloc method on the MFS pool. */ +static PoolAllocMethod mfs_alloc; -static void set_oom(Land land, int oom) + +/* oomAlloc -- allocation function that always fails + * + * Returns a randomly chosen memory error code. + */ + +static Res oomAlloc(Addr *pReturn, Pool pool, Size size) { - CBS cbs = PARENT(CBSStruct, landStruct, land); - cbs->blockPool->class = oom ? OOMPoolClassGet() : PoolClassMFS(); + MFS mfs = MustBeA(MFSPool, pool); + UNUSED(pReturn); + UNUSED(size); + if (mfs->extendSelf) { + /* This is the MFS block pool belonging to the CBS belonging to + * the MVFF or MVT pool under test, so simulate a failure to + * enforce the fail-over behaviour. */ + switch (rnd() % 3) { + case 0: + return ResRESOURCE; + case 1: + return ResMEMORY; + default: + return ResCOMMIT_LIMIT; + } + } else { + /* This is the MFS block pool belonging to the arena's free land, + * so succeed here (see job004041). */ + return mfs_alloc(pReturn, pool, size); + } } /* 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, Land cbs) + mps_align_t alignment, mps_pool_t pool) { mps_res_t res = MPS_RES_OK; mps_ap_t ap; @@ -113,11 +100,12 @@ static mps_res_t stress(size_t (*size)(unsigned long, mps_align_t), /* allocate a load of objects */ for (i=0; i= sizeof(ps[i])) *ps[i] = 1; /* Write something, so it gets swap. */ } @@ -143,15 +131,17 @@ static mps_res_t stress(size_t (*size)(unsigned long, mps_align_t), } /* allocate some new objects */ for (i=testSetSIZE/2; i. + * Copyright (c) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/freelist.c b/mps/code/freelist.c index 6eddc3dff83..b3e2fde3112 100644 --- a/mps/code/freelist.c +++ b/mps/code/freelist.c @@ -13,7 +13,6 @@ SRCID(freelist, "$Id$"); -#define freelistOfLand(land) PARENT(FreelistStruct, landStruct, land) #define freelistAlignment(fl) LandAlignment(FreelistLand(fl)) @@ -187,51 +186,45 @@ Bool FreelistCheck(Freelist fl) } -static Res freelistInit(Land land, ArgList args) +static Res freelistInit(Land land, Arena arena, Align alignment, ArgList args) { Freelist fl; - LandClass super; Res res; - AVERT(Land, land); - super = LAND_SUPERCLASS(FreelistLandClass); - res = (*super->init)(land, args); + AVER(land != NULL); + res = NextMethod(Land, Freelist, init)(land, arena, alignment, args); if (res != ResOK) return res; + fl = CouldBeA(Freelist, land); /* See */ AVER(AlignIsAligned(LandAlignment(land), FreelistMinimumAlignment)); - fl = freelistOfLand(land); fl->list = freelistEND; fl->listSize = 0; fl->size = 0; + SetClassOfPoly(land, CLASS(Freelist)); fl->sig = FreelistSig; - AVERT(Freelist, fl); + AVERC(Freelist, fl); + return ResOK; } -static void freelistFinish(Land land) +static void freelistFinish(Inst inst) { - Freelist fl; - - AVERT(Land, land); - fl = freelistOfLand(land); - AVERT(Freelist, fl); + Land land = MustBeA(Land, inst); + Freelist fl = MustBeA(Freelist, land); fl->sig = SigInvalid; fl->list = freelistEND; + NextMethod(Inst, Freelist, finish)(inst); } static Size freelistSize(Land land) { - Freelist fl; - - AVERT(Land, land); - fl = freelistOfLand(land); - AVERT(Freelist, fl); + Freelist fl = MustBeA(Freelist, land); return fl->size; } @@ -277,15 +270,12 @@ static void freelistBlockSetPrevNext(Freelist fl, FreelistBlock prev, static Res freelistInsert(Range rangeReturn, Land land, Range range) { - Freelist fl; + Freelist fl = MustBeA(Freelist, land); 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, freelistAlignment(fl))); @@ -404,14 +394,11 @@ static void freelistDeleteFromBlock(Range rangeReturn, Freelist fl, static Res freelistDelete(Range rangeReturn, Land land, Range range) { - Freelist fl; + Freelist fl = MustBeA(Freelist, land); 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); @@ -444,16 +431,13 @@ static Res freelistDelete(Range rangeReturn, Land land, Range range) static Bool freelistIterate(Land land, LandVisitor visitor, - void *closureP, Size closureS) + void *closure) { - Freelist fl; + Freelist fl = MustBeA(Freelist, land); FreelistBlock cur, next; - AVERT(Land, land); - fl = freelistOfLand(land); - AVERT(Freelist, fl); AVER(FUNCHECK(visitor)); - /* closureP and closureS are arbitrary */ + /* closure arbitrary */ for (cur = fl->list; cur != freelistEND; cur = next) { RangeStruct range; @@ -462,7 +446,7 @@ static Bool freelistIterate(Land land, LandVisitor visitor, * visitor touches the block. */ next = freelistBlockNext(cur); RangeInit(&range, freelistBlockBase(cur), freelistBlockLimit(fl, cur)); - cont = (*visitor)(land, &range, closureP, closureS); + cont = (*visitor)(land, &range, closure); if (!cont) return FALSE; } @@ -471,16 +455,13 @@ static Bool freelistIterate(Land land, LandVisitor visitor, static Bool freelistIterateAndDelete(Land land, LandDeleteVisitor visitor, - void *closureP, Size closureS) + void *closure) { - Freelist fl; + Freelist fl = MustBeA(Freelist, land); FreelistBlock prev, cur, next; - AVERT(Land, land); - fl = freelistOfLand(land); - AVERT(Freelist, fl); AVER(FUNCHECK(visitor)); - /* closureP and closureS are arbitrary */ + /* closure arbitrary */ prev = freelistEND; cur = fl->list; @@ -492,7 +473,7 @@ static Bool freelistIterateAndDelete(Land land, LandDeleteVisitor visitor, next = freelistBlockNext(cur); /* See .next.first. */ size = freelistBlockSize(fl, cur); RangeInit(&range, freelistBlockBase(cur), freelistBlockLimit(fl, cur)); - cont = (*visitor)(&delete, land, &range, closureP, closureS); + cont = (*visitor)(&delete, land, &range, closure); if (delete) { freelistBlockSetPrevNext(fl, prev, next, -1); AVER(fl->size >= size); @@ -573,14 +554,11 @@ static void freelistFindDeleteFromBlock(Range rangeReturn, Range oldRangeReturn, static Bool freelistFindFirst(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) { - Freelist fl; + Freelist fl = MustBeA(Freelist, land); FreelistBlock prev, cur, next; AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); - AVERT(Land, land); - fl = freelistOfLand(land); - AVERT(Freelist, fl); AVER(SizeIsAligned(size, freelistAlignment(fl))); AVERT(FindDelete, findDelete); @@ -604,16 +582,13 @@ static Bool freelistFindFirst(Range rangeReturn, Range oldRangeReturn, static Bool freelistFindLast(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) { - Freelist fl; + Freelist fl = MustBeA(Freelist, land); Bool found = FALSE; FreelistBlock prev, cur, next; FreelistBlock foundPrev = freelistEND, foundCur = freelistEND; AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); - AVERT(Land, land); - fl = freelistOfLand(land); - AVERT(Freelist, fl); AVER(SizeIsAligned(size, freelistAlignment(fl))); AVERT(FindDelete, findDelete); @@ -641,16 +616,13 @@ static Bool freelistFindLast(Range rangeReturn, Range oldRangeReturn, static Bool freelistFindLargest(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) { - Freelist fl; + Freelist fl = MustBeA(Freelist, land); Bool found = FALSE; FreelistBlock prev, cur, next; FreelistBlock bestPrev = freelistEND, bestCur = freelistEND; AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); - AVERT(Land, land); - fl = freelistOfLand(land); - AVERT(Freelist, fl); AVERT(FindDelete, findDelete); prev = freelistEND; @@ -679,7 +651,7 @@ static Res freelistFindInZones(Bool *foundReturn, Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high) { - Freelist fl; + Freelist fl = MustBeA(Freelist, land); LandFindMethod landFind; RangeInZoneSet search; Bool found = FALSE; @@ -690,9 +662,6 @@ static Res freelistFindInZones(Bool *foundReturn, Range rangeReturn, 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); @@ -746,24 +715,28 @@ static Res freelistFindInZones(Bool *foundReturn, Range rangeReturn, /* freelistDescribeVisitor -- visitor method for freelistDescribe * * Writes a decription of the range into the stream pointed to by - * closureP. + * closure. */ +typedef struct FreelistDescribeClosureStruct { + mps_lib_FILE *stream; + Count depth; +} FreelistDescribeClosureStruct, *FreelistDescribeClosure; + static Bool freelistDescribeVisitor(Land land, Range range, - void *closureP, Size closureS) + void *closure) { Res res; - mps_lib_FILE *stream = closureP; - Count depth = closureS; + FreelistDescribeClosure my = closure; if (!TESTT(Land, land)) return FALSE; if (!RangeCheck(range)) return FALSE; - if (stream == NULL) + if (my->stream == NULL) return FALSE; - res = WriteF(stream, depth, + res = WriteF(my->stream, my->depth, "[$P,", (WriteFP)RangeBase(range), "$P)", (WriteFP)RangeLimit(range), " {$U}\n", (WriteFU)RangeSize(range), @@ -773,53 +746,55 @@ static Bool freelistDescribeVisitor(Land land, Range range, } -static Res freelistDescribe(Land land, mps_lib_FILE *stream, Count depth) +static Res freelistDescribe(Inst inst, mps_lib_FILE *stream, Count depth) { - Freelist fl; + Land land = CouldBeA(Land, inst); + Freelist fl = CouldBeA(Freelist, land); Res res; Bool b; + FreelistDescribeClosureStruct closure; - if (!TESTT(Land, land)) - return ResFAIL; - fl = freelistOfLand(land); - if (!TESTT(Freelist, fl)) - return ResFAIL; + if (!TESTC(Freelist, fl)) + return ResPARAM; if (stream == NULL) - return ResFAIL; + return ResPARAM; - res = WriteF(stream, depth, - "Freelist $P {\n", (WriteFP)fl, - " listSize = $U\n", (WriteFU)fl->listSize, - " size = $U\n", (WriteFU)fl->size, + res = NextMethod(Inst, Freelist, describe)(inst, stream, depth); + if (res != ResOK) + return res; + + res = WriteF(stream, depth + 2, + "listSize $U\n", (WriteFU)fl->listSize, + "size $U\n", (WriteFU)fl->size, NULL); - b = LandIterate(land, freelistDescribeVisitor, stream, depth + 2); + closure.stream = stream; + closure.depth = depth + 2; + b = LandIterate(land, freelistDescribeVisitor, &closure); if (!b) return ResFAIL; - res = WriteF(stream, depth, "} Freelist $P\n", (WriteFP)fl, NULL); return res; } -DEFINE_LAND_CLASS(FreelistLandClass, class) +DEFINE_CLASS(Land, Freelist, klass) { - 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); + INHERIT_CLASS(klass, Freelist, Land); + klass->instClassStruct.describe = freelistDescribe; + klass->instClassStruct.finish = freelistFinish; + klass->size = sizeof(FreelistStruct); + klass->init = freelistInit; + klass->sizeMethod = freelistSize; + klass->insert = freelistInsert; + klass->delete = freelistDelete; + klass->iterate = freelistIterate; + klass->iterateAndDelete = freelistIterateAndDelete; + klass->findFirst = freelistFindFirst; + klass->findLast = freelistFindLast; + klass->findLargest = freelistFindLargest; + klass->findInZones = freelistFindInZones; + AVERT(LandClass, klass); } diff --git a/mps/code/freelist.h b/mps/code/freelist.h index dab791c9c03..6b84564e0ab 100644 --- a/mps/code/freelist.h +++ b/mps/code/freelist.h @@ -10,6 +10,8 @@ #define freelist_h #include "mpmtypes.h" +#include "mpm.h" +#include "protocol.h" typedef struct FreelistStruct *Freelist; @@ -20,7 +22,7 @@ extern Bool FreelistCheck(Freelist freelist); /* See */ #define FreelistMinimumAlignment ((Align)sizeof(FreelistBlock)) -extern LandClass FreelistLandClassGet(void); +DECLARE_CLASS(Land, Freelist, Land); #endif /* freelist.h */ diff --git a/mps/code/fri3gc.gmk b/mps/code/fri3gc.gmk index 99d455e51fa..3bcbf187ef1 100644 --- a/mps/code/fri3gc.gmk +++ b/mps/code/fri3gc.gmk @@ -3,19 +3,19 @@ # fri3gc.gmk: BUILD FOR FreeBSD/i386/GCC PLATFORM # # $Id$ -# Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. PFM = fri3gc MPMPF = \ lockix.c \ - prmcan.c \ - prmci3fr.c \ + prmcanan.c \ + prmcfri3.c \ + prmcix.c \ protix.c \ protsgix.c \ pthrdext.c \ span.c \ - ssixi3.c \ thix.c \ vmix.c @@ -32,7 +32,7 @@ include comm.gmk # C. COPYRIGHT AND LICENSE # -# Copyright (C) 2001-2014 Ravenbrook Limited . +# Copyright (C) 2001-2016 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. # diff --git a/mps/code/fri3ll.gmk b/mps/code/fri3ll.gmk index 8801830e757..82389f7ebbe 100644 --- a/mps/code/fri3ll.gmk +++ b/mps/code/fri3ll.gmk @@ -3,19 +3,19 @@ # fri3ll.gmk: BUILD FOR FreeBSD/i386/GCC PLATFORM # # $Id$ -# Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. PFM = fri3ll MPMPF = \ lockix.c \ - prmcan.c \ - prmci3fr.c \ + prmcanan.c \ + prmcfri3.c \ + prmcix.c \ protix.c \ protsgix.c \ pthrdext.c \ span.c \ - ssixi3.c \ thix.c \ vmix.c @@ -32,7 +32,7 @@ include comm.gmk # C. COPYRIGHT AND LICENSE # -# Copyright (C) 2001-2014 Ravenbrook Limited . +# Copyright (C) 2001-2016 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. # diff --git a/mps/code/fri6gc.gmk b/mps/code/fri6gc.gmk index a0cb6270c12..7d165182468 100644 --- a/mps/code/fri6gc.gmk +++ b/mps/code/fri6gc.gmk @@ -3,33 +3,36 @@ # fri6gc.gmk: BUILD FOR FreeBSD/x86-64/GCC PLATFORM # # $Id$ -# Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. PFM = fri6gc -MPMPF = lockix.c thix.c pthrdext.c vmix.c \ - protix.c protsgix.c prmcan.c prmci6fr.c ssixi6.c span.c +MPMPF = \ + lockix.c \ + prmcanan.c \ + prmcfri6.c \ + prmcix.c \ + protix.c \ + protsgix.c \ + pthrdext.c \ + span.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-2016 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. # diff --git a/mps/code/fri6ll.gmk b/mps/code/fri6ll.gmk index 6595410c9a3..faccd6d928b 100644 --- a/mps/code/fri6ll.gmk +++ b/mps/code/fri6ll.gmk @@ -1,29 +1,32 @@ # -*- makefile -*- # -# fri6ll.gmk: BUILD FOR FreeBSD/x86-64/GCC PLATFORM +# fri6ll.gmk: BUILD FOR FreeBSD/x86-64/Clang PLATFORM # # $Id$ # Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. PFM = fri6ll -MPMPF = lockix.c thix.c pthrdext.c vmix.c \ - protix.c protsgix.c prmcan.c prmci6fr.c ssixi6.c span.c +MPMPF = \ + lockix.c \ + prmcanan.c \ + prmcfri6.c \ + prmcix.c \ + protix.c \ + protsgix.c \ + pthrdext.c \ + span.c \ + thix.c \ + vmix.c LIBS = -lm -pthread include ll.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 diff --git a/mps/code/gc.gmk b/mps/code/gc.gmk index 76716dc0785..f871b4f5c8b 100644 --- a/mps/code/gc.gmk +++ b/mps/code/gc.gmk @@ -13,21 +13,21 @@ CC = gcc CFLAGSDEBUG = -O -g3 CFLAGSOPT = -O2 -g3 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 +-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 := -std=c89 -pedantic # A different set of compiler flags for less strict compilation, for # instance when we need to #include a third-party header file that @@ -41,9 +41,9 @@ CFLAGSCOMPILERLAX := # If interrupted, this is liable to leave a zero-length file behind. define gendep - $(SHELL) -ec "$(CC) $(CFLAGSSTRICT) -MM $< | \ - sed '/:/s!$*.o!$(@D)/& $(@D)/$*.d!' > $@" - [ -s $@ ] || rm -f $@ +$(SHELL) -ec "$(CC) $(CFLAGSSTRICT) -MM $< | \ + sed '/:/s!$*.o!$(@D)/& $(@D)/$*.d!' > $@" +[ -s $@ ] || rm -f $@ endef diff --git a/mps/code/gcbench.c b/mps/code/gcbench.c index 8624637da4a..40612fbfab8 100644 --- a/mps/code/gcbench.c +++ b/mps/code/gcbench.c @@ -1,7 +1,7 @@ /* gcbench.c -- "GC" Benchmark on ANSI C library * * $Id$ - * Copyright 2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2014-2018 Ravenbrook Limited. See end of file for license. * * This is an allocation stress benchmark test for gc pools */ @@ -55,6 +55,7 @@ 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 */ +static double pause_time = ARENA_DEFAULT_PAUSE_TIME; /* maximum pause time */ typedef struct gcthread_s *gcthread_t; @@ -70,22 +71,26 @@ struct gcthread_s { typedef mps_word_t obj_t; -static obj_t mkvector(mps_ap_t ap, size_t n) { +static obj_t mkvector(mps_ap_t ap, size_t n) +{ mps_word_t v; RESMUST(make_dylan_vector(&v, ap, n)); return v; } -static obj_t aref(obj_t v, size_t i) { +static obj_t aref(obj_t v, size_t i) +{ return DYLAN_VECTOR_SLOT(v, i); } -static void aset(obj_t v, size_t i, obj_t val) { +static void aset(obj_t v, size_t i, obj_t val) +{ DYLAN_VECTOR_SLOT(v, i) = val; } /* mktree - make a tree of nodes with depth d. */ -static obj_t mktree(mps_ap_t ap, unsigned d, obj_t leaf) { +static obj_t mktree(mps_ap_t ap, unsigned d, obj_t leaf) +{ obj_t tree; size_t i; if (d <= 0) @@ -97,7 +102,8 @@ static obj_t mktree(mps_ap_t ap, unsigned d, obj_t leaf) { return tree; } -static obj_t random_subtree(obj_t tree, unsigned levels) { +static obj_t random_subtree(obj_t tree, unsigned levels) +{ while(tree != objNULL && levels > 0) { tree = aref(tree, rnd() % width); --levels; @@ -113,7 +119,8 @@ static obj_t random_subtree(obj_t tree, unsigned levels) { * NOTE: Changing preuse will dramatically change how much work * is done. In particular, if preuse==1, the old tree is returned * unchanged. */ -static obj_t new_tree(mps_ap_t ap, obj_t oldtree, unsigned d) { +static obj_t new_tree(mps_ap_t ap, obj_t oldtree, unsigned d) +{ obj_t subtree; size_t i; if (rnd_double() < preuse) { @@ -132,7 +139,8 @@ static obj_t new_tree(mps_ap_t ap, obj_t oldtree, unsigned d) { /* Update tree to be identical tree but with nodes reallocated * with probability pupdate. This avoids writing to vector slots * if unecessary. */ -static obj_t update_tree(mps_ap_t ap, obj_t oldtree, unsigned d) { +static obj_t update_tree(mps_ap_t ap, obj_t oldtree, unsigned d) +{ obj_t tree; size_t i; if (oldtree == objNULL || d == 0) @@ -155,7 +163,8 @@ static obj_t update_tree(mps_ap_t ap, obj_t oldtree, unsigned d) { return tree; } -static void *gc_tree(gcthread_t thread) { +static void *gc_tree(gcthread_t thread) +{ unsigned i, j; mps_ap_t ap = thread->ap; obj_t leaf = pinleaf ? mktree(ap, 1, objNULL) : objNULL; @@ -172,7 +181,8 @@ static void *gc_tree(gcthread_t thread) { } /* start -- start routine for each thread */ -static void *start(void *p) { +static void *start(void *p) +{ gcthread_t thread = p; void *marker; RESMUST(mps_thread_reg(&thread->mps_thread, arena)); @@ -235,6 +245,7 @@ static void arena_setup(gcthread_fn_t fn, 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); + MPS_ARGS_ADD(args, MPS_KEY_PAUSE_TIME, pause_time); RESMUST(mps_arena_create_k(&arena, mps_arena_class_vm(), args)); } MPS_ARGS_END(args); RESMUST(dylan_fmt(&format, arena)); @@ -251,8 +262,6 @@ static void arena_setup(gcthread_fn_t fn, } 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) @@ -278,6 +287,7 @@ static struct option longopts[] = { {"pin-leaf", no_argument, NULL, 'l'}, {"seed", required_argument, NULL, 'x'}, {"arena-unzoned", no_argument, NULL, 'z'}, + {"pause-time", required_argument, NULL, 'P'}, {NULL, 0, NULL, 0 } }; @@ -289,25 +299,22 @@ static struct { } pools[] = { {"amc", gc_tree, mps_class_amc}, {"ams", gc_tree, mps_class_ams}, + {"awl", gc_tree, mps_class_awl}, }; /* Command-line driver */ -int main(int argc, char *argv[]) { +int main(int argc, char *argv[]) +{ int ch; unsigned i; - int k; + mps_bool_t seed_specified = FALSE; seed = rnd_seed(); - for(k=0; k 0) { for (i = 0; i < NELEMS(pools); ++i) @@ -472,7 +488,7 @@ int main(int argc, char *argv[]) { /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2014 Ravenbrook Limited . + * Copyright (c) 2014-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/global.c b/mps/code/global.c index 354adb54a9a..a69dad836cb 100644 --- a/mps/code/global.c +++ b/mps/code/global.c @@ -1,7 +1,7 @@ /* global.c: ARENA-GLOBAL INTERFACES * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. * * .sources: See . design.mps.thread-safety is relevant @@ -24,7 +24,6 @@ #include "bt.h" #include "poolmrg.h" #include "mps.h" /* finalization */ -#include "poolmv.h" #include "mpm.h" SRCID(global, "$Id$"); @@ -53,6 +52,47 @@ static void arenaReleaseRingLock(void) } +/* GlobalsClaimAll -- claim all MPS locks + * + */ + +void GlobalsClaimAll(void) +{ + LockClaimGlobalRecursive(); + arenaClaimRingLock(); + GlobalsArenaMap(ArenaEnter); +} + +/* GlobalsReleaseAll -- release all MPS locks. GlobalsClaimAll must + * previously have been called. */ + +void GlobalsReleaseAll(void) +{ + GlobalsArenaMap(ArenaLeave); + arenaReleaseRingLock(); + LockReleaseGlobalRecursive(); +} + +/* arenaReinitLock -- reinitialize the lock for an arena */ + +static void arenaReinitLock(Arena arena) +{ + AVERT(Arena, arena); + ShieldLeave(arena); + LockInit(ArenaGlobals(arena)->lock); +} + +/* GlobalsReinitializeAll -- reinitialize all MPS locks, and leave the + * shield for all arenas. GlobalsClaimAll must previously have been + * called. */ + +void GlobalsReinitializeAll(void) +{ + GlobalsArenaMap(arenaReinitLock); + LockInitGlobal(); +} + + /* arenaAnnounce -- add a new arena into the global ring of arenas * * On entry, the arena must not be locked (there should be no need, @@ -100,6 +140,21 @@ static void arenaDenounce(Arena arena) } +/* GlobalsArenaMap -- map a function over the arenas. The caller must + * have acquired the ring lock. */ + +void GlobalsArenaMap(void (*func)(Arena arena)) +{ + Ring node, nextNode; + AVERT(Ring, &arenaRing); + RING_FOR(node, &arenaRing, nextNode) { + Globals arenaGlobals = RING_ELT(Globals, globalRing, node); + Arena arena = GlobalsArena(arenaGlobals); + func(arena); + } +} + + /* GlobalsCheck -- check the arena globals */ Bool GlobalsCheck(Globals arenaGlobals) @@ -107,9 +162,6 @@ Bool GlobalsCheck(Globals arenaGlobals) Arena arena; TraceId ti; Trace trace; - Index i; - Size depth; - RefSet rs; Rank rank; CHECKS(Globals, arenaGlobals); @@ -155,20 +207,7 @@ Bool GlobalsCheck(Globals arenaGlobals) CHECKD_NOSIG(Ring, &arena->threadRing); CHECKD_NOSIG(Ring, &arena->deadRing); - CHECKL(BoolCheck(arena->insideShield)); - CHECKL(arena->shCacheLimit <= ShieldCacheSIZE); - CHECKL(arena->shCacheI < arena->shCacheLimit); - CHECKL(BoolCheck(arena->suspended)); - - depth = 0; - for (i = 0; i < arena->shCacheLimit; ++i) { - Seg seg = arena->shCache[i]; - if (seg != NULL) { - CHECKD(Seg, seg); - depth += SegDepth(seg); - } - } - CHECKL(depth <= arena->shDepth); + CHECKD(Shield, ArenaShield(arena)); CHECKL(TraceSetCheck(arena->busyTraces)); CHECKL(TraceSetCheck(arena->flippedTraces)); @@ -190,23 +229,12 @@ Bool GlobalsCheck(Globals arenaGlobals) CHECKD_NOSIG(Ring, &arena->greyRing[rank]); CHECKD_NOSIG(Ring, &arena->chainRing); - CHECKL(arena->tracedSize >= 0.0); + CHECKL(arena->tracedWork >= 0.0); CHECKL(arena->tracedTime >= 0.0); /* no check for arena->lastWorldCollect (Clock) */ /* can't write a check for arena->epoch */ - - /* check that each history entry is a subset of the next oldest */ - rs = RefSetEMPTY; - /* note this loop starts from 1; there is no history age 0 */ - for (i=1; i <= LDHistoryLENGTH; ++ i) { - /* check history age 'i'; 'j' is the history index. */ - Index j = (arena->epoch + LDHistoryLENGTH - i) % LDHistoryLENGTH; - CHECKL(RefSetSub(rs, arena->history[j])); - rs = arena->history[j]; - } - /* the oldest history entry must be a subset of the prehistory */ - CHECKL(RefSetSub(rs, arena->prehistory)); + CHECKD(History, ArenaHistory(arena)); /* we also check the statics now. */ CHECKL(BoolCheck(arenaRingInit)); @@ -215,12 +243,15 @@ Bool GlobalsCheck(Globals arenaGlobals) CHECKL(RingCheck(&arenaRing)); CHECKL(BoolCheck(arena->emergency)); + /* .emergency.invariant: There can only be an emergency when a trace + * is busy. */ + CHECKL(!arena->emergency || arena->busyTraces != TraceSetEMPTY); if (arenaGlobals->defaultChain != NULL) CHECKD(Chain, arenaGlobals->defaultChain); - /* can't check arena->stackAtArenaEnter */ - + /* can't check arena->stackWarm */ + return TRUE; } @@ -230,7 +261,6 @@ Bool GlobalsCheck(Globals arenaGlobals) Res GlobalsInit(Globals arenaGlobals) { Arena arena; - Index i; Rank rank; TraceId ti; @@ -246,7 +276,13 @@ Res GlobalsInit(Globals arenaGlobals) arenaRingInit = TRUE; RingInit(&arenaRing); arenaSerial = (Serial)0; + /* The setup functions call pthread_atfork (on the appropriate + platforms) and so must be called in the correct order. Here we + require the locks to be taken first in the "prepare" case and + released last in the "parent" and "child" cases. */ + ThreadSetup(); ProtSetup(); + LockSetup(); } arena = GlobalsArena(arenaGlobals); /* Ensure updates to arenaSerial do not race by doing the update @@ -272,6 +308,11 @@ Res GlobalsInit(Globals arenaGlobals) arenaGlobals->bufferLogging = FALSE; RingInit(&arenaGlobals->poolRing); arenaGlobals->poolSerial = (Serial)0; + /* The system pools are: + 1. arena->freeCBSBlockPoolStruct + 2. arena->controlPoolStruct + 3. arena->controlPoolStruct.cbsBlockPoolStruct */ + arenaGlobals->systemPools = (Count)3; RingInit(&arenaGlobals->rootRing); arenaGlobals->rootSerial = (Serial)0; RingInit(&arenaGlobals->rememberedSummaryRing); @@ -289,16 +330,10 @@ Res GlobalsInit(Globals arenaGlobals) arena->finalPool = NULL; arena->busyTraces = TraceSetEMPTY; /* */ arena->flippedTraces = TraceSetEMPTY; /* */ - arena->tracedSize = 0.0; + arena->tracedWork = 0.0; arena->tracedTime = 0.0; arena->lastWorldCollect = ClockNow(); - arena->insideShield = FALSE; /* */ - arena->shCacheI = (Size)0; - arena->shCacheLimit = (Size)1; - arena->shDepth = (Size)0; - arena->suspended = FALSE; - for(i = 0; i < ShieldCacheSIZE; i++) - arena->shCache[i] = NULL; + ShieldInit(ArenaShield(arena)); for (ti = 0; ti < TraceLIMIT; ++ti) { /* */ @@ -315,14 +350,11 @@ Res GlobalsInit(Globals arenaGlobals) STATISTIC(arena->writeBarrierHitCount = 0); RingInit(&arena->chainRing); - arena->epoch = (Epoch)0; /* */ - arena->prehistory = RefSetEMPTY; - for(i = 0; i < LDHistoryLENGTH; ++i) - arena->history[i] = RefSetEMPTY; - + HistoryInit(ArenaHistory(arena)); + arena->emergency = FALSE; - arena->stackAtArenaEnter = NULL; + arena->stackWarm = NULL; arenaGlobals->defaultChain = NULL; @@ -352,7 +384,7 @@ Res GlobalsCompleteCreate(Globals arenaGlobals) { void *v; - res = ControlAlloc(&v, arena, BTSize(MessageTypeLIMIT), FALSE); + res = ControlAlloc(&v, arena, BTSize(MessageTypeLIMIT)); if (res != ResOK) return res; arena->enabledMessageTypes = v; @@ -366,7 +398,7 @@ Res GlobalsCompleteCreate(Globals arenaGlobals) return res; TRACE_SET_ITER_END(ti, trace, TraceSetUNIV, arena); - res = ControlAlloc(&p, arena, LockSize(), FALSE); + res = ControlAlloc(&p, arena, LockSize()); if (res != ResOK) return res; arenaGlobals->lock = (Lock)p; @@ -398,11 +430,12 @@ void GlobalsFinish(Globals arenaGlobals) arena = GlobalsArena(arenaGlobals); AVERT(Globals, arenaGlobals); - STATISTIC_STAT(EVENT2(ArenaWriteFaults, arena, - arena->writeBarrierHitCount)); + STATISTIC(EVENT2(ArenaWriteFaults, arena, arena->writeBarrierHitCount)); arenaGlobals->sig = SigInvalid; + ShieldFinish(ArenaShield(arena)); + HistoryFinish(ArenaHistory(arena)); RingFinish(&arena->formatRing); RingFinish(&arena->chainRing); RingFinish(&arena->messageRing); @@ -435,6 +468,7 @@ void GlobalsPrepareToDestroy(Globals arenaGlobals) ArenaPark(arenaGlobals); arena = GlobalsArena(arenaGlobals); + arenaDenounce(arena); defaultChain = arenaGlobals->defaultChain; @@ -486,6 +520,8 @@ void GlobalsPrepareToDestroy(Globals arenaGlobals) PoolDestroy(pool); } + ShieldDestroyQueue(ArenaShield(arena), arena); + /* 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 @@ -494,23 +530,15 @@ void GlobalsPrepareToDestroy(Globals arenaGlobals) * 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->formatRing)); /* */ + AVER(RingIsSingle(&arena->chainRing)); /* */ AVER(RingIsSingle(&arena->messageRing)); - AVER(RingIsSingle(&arena->threadRing)); + AVER(RingIsSingle(&arena->threadRing)); /* */ AVER(RingIsSingle(&arena->deadRing)); - AVER(RingIsSingle(&arenaGlobals->rootRing)); + 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); + AVER(RingLength(&arenaGlobals->poolRing) == arenaGlobals->systemPools); /* */ } @@ -524,10 +552,9 @@ Ring GlobalsRememberedSummaryRing(Globals global) /* ArenaEnter -- enter the state where you can look at the arena */ -void (ArenaEnter)(Arena arena) +void ArenaEnter(Arena arena) { - AVERT(Arena, arena); - ArenaEnter(arena); + ArenaEnterLock(arena, FALSE); } /* The recursive argument specifies whether to claim the lock @@ -558,7 +585,6 @@ void ArenaEnterLock(Arena arena, Bool recursive) } else { ShieldEnter(arena); } - return; } /* Same as ArenaEnter, but for the few functions that need to be @@ -572,10 +598,10 @@ void ArenaEnterRecursive(Arena arena) /* ArenaLeave -- leave the state where you can look at MPM data structures */ -void (ArenaLeave)(Arena arena) +void ArenaLeave(Arena arena) { AVERT(Arena, arena); - ArenaLeave(arena); + ArenaLeaveLock(arena, FALSE); } void ArenaLeaveLock(Arena arena, Bool recursive) @@ -597,7 +623,6 @@ void ArenaLeaveLock(Arena arena, Bool recursive) } else { LockRelease(lock); } - return; } void ArenaLeaveRecursive(Arena arena) @@ -605,14 +630,10 @@ void ArenaLeaveRecursive(Arena arena) ArenaLeaveLock(arena, TRUE); } -/* mps_exception_info -- pointer to exception info - * - * This is a hack to make exception info easier to find in a release - * version. The format is platform-specific. We won't necessarily - * publish this. */ - -extern MutatorFaultContext mps_exception_info; -MutatorFaultContext mps_exception_info = NULL; +Bool ArenaBusy(Arena arena) +{ + return LockIsHeld(ArenaGlobals(arena)->lock); +} /* ArenaAccess -- deal with an access fault @@ -621,7 +642,7 @@ MutatorFaultContext mps_exception_info = NULL; * corresponds to which mode flags need to be cleared in order for the * access to continue. */ -Bool ArenaAccess(Addr addr, AccessSet mode, MutatorFaultContext context) +Bool ArenaAccess(Addr addr, AccessSet mode, MutatorContext context) { static Count count = 0; /* used to match up ArenaAccess events */ Seg seg; @@ -629,7 +650,6 @@ Bool ArenaAccess(Addr addr, AccessSet mode, MutatorFaultContext context) Res res; arenaClaimRingLock(); /* */ - mps_exception_info = context; AVERT(Ring, &arenaRing); RING_FOR(node, &arenaRing, nextNode) { @@ -645,7 +665,6 @@ Bool ArenaAccess(Addr addr, AccessSet mode, MutatorFaultContext context) /* protected root on a segment. */ /* It is possible to overcome this restriction. */ if (SegOfAddr(&seg, arena, addr)) { - mps_exception_info = NULL; arenaReleaseRingLock(); /* An access in a different thread (or even in the same thread, * via a signal or exception handler) may have already caused @@ -654,7 +673,7 @@ Bool ArenaAccess(Addr addr, AccessSet mode, MutatorFaultContext context) * thread. */ mode &= SegPM(seg); if (mode != AccessSetEMPTY) { - res = PoolAccess(SegPool(seg), seg, addr, mode, context); + res = SegAccess(seg, arena, addr, mode, context); AVER(res == ResOK); /* Mutator can't continue unless this succeeds */ } else { /* Protection was already cleared, for example by another thread @@ -664,7 +683,6 @@ Bool ArenaAccess(Addr addr, AccessSet mode, MutatorFaultContext context) ArenaLeave(arena); return TRUE; } else if (RootOfAddr(&root, arena, addr)) { - mps_exception_info = NULL; arenaReleaseRingLock(); mode &= RootPM(root); if (mode != AccessSetEMPTY) @@ -682,7 +700,6 @@ Bool ArenaAccess(Addr addr, AccessSet mode, MutatorFaultContext context) ArenaLeave(arena); } - mps_exception_info = NULL; arenaReleaseRingLock(); return FALSE; } @@ -708,8 +725,9 @@ void (ArenaPoll)(Globals globals) { Arena arena; Clock start; - Count quanta; - Size tracedSize; + Bool worldCollected = FALSE; + Bool moreWork, workWasDone = FALSE; + Work tracedWork; AVERT(Globals, globals); @@ -725,35 +743,34 @@ void (ArenaPoll)(Globals globals) /* fillMutatorSize has advanced; call TracePoll enough to catch up. */ start = ClockNow(); - quanta = 0; - EVENT3(ArenaPoll, arena, start, 0); + EVENT3(ArenaPoll, arena, start, FALSE); do { - tracedSize = TracePoll(globals); - if (tracedSize > 0) { - quanta += 1; - arena->tracedSize += tracedSize; + moreWork = TracePoll(&tracedWork, &worldCollected, globals, + !worldCollected); + if (moreWork) { + workWasDone = TRUE; } - } while (PolicyPollAgain(arena, start, tracedSize)); + } while (PolicyPollAgain(arena, start, moreWork, tracedWork)); /* Don't count time spent checking for work, if there was no work to do. */ - if(quanta > 0) { - arena->tracedTime += (ClockNow() - start) / (double) ClocksPerSec(); + if (workWasDone) { + ArenaAccumulateTime(arena, start, ClockNow()); } - AVER(!PolicyPoll(arena)); - - EVENT3(ArenaPoll, arena, start, quanta); + EVENT3(ArenaPoll, arena, start, BOOLOF(workWasDone)); globals->insidePoll = FALSE; } + +/* ArenaStep -- use idle time for collection work */ + Bool ArenaStep(Globals globals, double interval, double multiplier) { - Size scanned; - Bool stepped; - Clock start, end, now; + Bool workWasDone = FALSE; + Clock start, intervalEnd, availableEnd, now; Clock clocks_per_sec; Arena arena; @@ -764,39 +781,46 @@ Bool ArenaStep(Globals globals, double interval, double multiplier) arena = GlobalsArena(globals); clocks_per_sec = ClocksPerSec(); - start = ClockNow(); - end = start + (Clock)(interval * clocks_per_sec); - AVER(end >= start); - - stepped = FALSE; - - if (PolicyShouldCollectWorld(arena, interval, multiplier, - start, clocks_per_sec)) - { - Res res; - Trace trace; - res = TraceStartCollectAll(&trace, arena, TraceStartWhyOPPORTUNISM); - if (res == ResOK) { - arena->lastWorldCollect = start; - stepped = TRUE; - } - } + start = now = ClockNow(); + intervalEnd = start + (Clock)(interval * clocks_per_sec); + AVER(intervalEnd >= start); + availableEnd = start + (Clock)(interval * multiplier * clocks_per_sec); + AVER(availableEnd >= start); /* loop while there is work to do and time on the clock. */ do { - scanned = TracePoll(globals); - now = ClockNow(); - if (scanned > 0) { - stepped = TRUE; - arena->tracedSize += scanned; + Trace trace; + if (arena->busyTraces != TraceSetEMPTY) { + trace = ArenaTrace(arena, (TraceId)0); + } else { + /* No traces are running: consider collecting the world. */ + if (PolicyShouldCollectWorld(arena, (double)(availableEnd - now), now, + clocks_per_sec)) + { + Res res; + res = TraceStartCollectAll(&trace, arena, TraceStartWhyOPPORTUNISM); + if (res != ResOK) + break; + arena->lastWorldCollect = now; + } else { + /* Not worth collecting the world; consider starting a trace. */ + Bool worldCollected; + if (!PolicyStartTrace(&trace, &worldCollected, arena, FALSE)) + break; + } } - } while ((scanned > 0) && (now < end)); + TraceAdvance(trace); + if (trace->state == TraceFINISHED) + TraceDestroyFinished(trace); + workWasDone = TRUE; + now = ClockNow(); + } while (now < intervalEnd); - if (stepped) { - arena->tracedTime += (now - start) / (double) clocks_per_sec; + if (workWasDone) { + ArenaAccumulateTime(arena, start, now); } - return stepped; + return workWasDone; } /* ArenaFinalize -- registers an object for finalization @@ -846,7 +870,7 @@ Res ArenaDefinalize(Arena arena, Ref obj) } -/* Peek / Poke */ +/* ArenaPeek -- read a single reference, possibly through a barrier */ Ref ArenaPeek(Arena arena, Ref *p) { @@ -854,6 +878,7 @@ Ref ArenaPeek(Arena arena, Ref *p) Ref ref; AVERT(Arena, arena); + /* Can't check p as it is arbitrary */ if (SegOfAddr(&seg, arena, (Addr)p)) ref = ArenaPeekSeg(arena, seg, p); @@ -862,74 +887,19 @@ Ref ArenaPeek(Arena arena, Ref *p) return ref; } +/* ArenaPeekSeg -- as ArenaPeek, but p must be in seg. */ + Ref ArenaPeekSeg(Arena arena, Seg seg, Ref *p) { Ref ref; - - AVERT(Arena, arena); - AVERT(Seg, seg); - - AVER(SegBase(seg) <= (Addr)p); - AVER((Addr)p < SegLimit(seg)); - /* TODO: Consider checking addr's alignment using seg->pool->alignment */ - - ShieldExpose(arena, seg); - ref = *p; - ShieldCover(arena, seg); - return ref; -} - -void ArenaPoke(Arena arena, Ref *p, Ref ref) -{ - Seg seg; - - AVERT(Arena, arena); - /* Can't check addr as it is arbitrary */ - /* Can't check ref as it is arbitrary */ - - if (SegOfAddr(&seg, arena, (Addr)p)) - ArenaPokeSeg(arena, seg, p, ref); - else - *p = ref; -} - -void ArenaPokeSeg(Arena arena, Seg seg, Ref *p, Ref ref) -{ - RefSet summary; - - AVERT(Arena, arena); - AVERT(Seg, seg); - AVER(SegBase(seg) <= (Addr)p); - AVER((Addr)p < SegLimit(seg)); - /* TODO: Consider checking addr's alignment using seg->pool->alignment */ - /* ref is arbitrary and can't be checked */ - - ShieldExpose(arena, seg); - *p = ref; - summary = SegSummary(seg); - summary = RefSetAdd(arena, summary, (Addr)ref); - SegSetSummary(seg, summary); - ShieldCover(arena, seg); -} - - -/* ArenaRead -- read a single reference, possibly through a barrier - * - * This forms part of a software barrier. It provides fine-grain access - * to single references in segments. - * - * See also PoolSingleAccess and PoolSegAccess. */ - -Ref ArenaRead(Arena arena, Ref *p) -{ - Bool b; - Seg seg = NULL; /* suppress "may be used uninitialized" */ Rank rank; AVERT(Arena, arena); - - b = SegOfAddr(&seg, arena, (Addr)p); - AVER(b == TRUE); + AVERT(Seg, seg); + AVER(PoolArena(SegPool(seg)) == arena); + AVER(SegBase(seg) <= (Addr)p); + AVER((Addr)p < SegLimit(seg)); + /* TODO: Consider checking p's alignment using seg->pool->alignment */ /* .read.flipped: We AVER that the reference that we are reading */ /* refers to an object for which all the traces that the object is */ @@ -951,11 +921,81 @@ Ref ArenaRead(Arena arena, Ref *p) /* We don't need to update the Seg Summary as in PoolSingleAccess * because we are not changing it after it has been scanned. */ + + ShieldExpose(arena, seg); + ref = *p; + ShieldCover(arena, seg); + return ref; +} + +/* ArenaPoke -- write a single reference, possibly through a barrier */ + +void ArenaPoke(Arena arena, Ref *p, Ref ref) +{ + Seg seg; + + AVERT(Arena, arena); + /* Can't check p as it is arbitrary */ + /* Can't check ref as it is arbitrary */ + + if (SegOfAddr(&seg, arena, (Addr)p)) + ArenaPokeSeg(arena, seg, p, ref); + else + *p = ref; +} + +/* ArenaPokeSeg -- as ArenaPoke, but p must be in seg. */ + +void ArenaPokeSeg(Arena arena, Seg seg, Ref *p, Ref ref) +{ + RefSet summary; + + AVERT(Arena, arena); + AVERT(Seg, seg); + AVER(PoolArena(SegPool(seg)) == arena); + AVER(SegBase(seg) <= (Addr)p); + AVER((Addr)p < SegLimit(seg)); + /* TODO: Consider checking p's alignment using seg->pool->alignment */ + /* ref is arbitrary and can't be checked */ + + ShieldExpose(arena, seg); + *p = ref; + summary = SegSummary(seg); + summary = RefSetAdd(arena, summary, (Addr)ref); + SegSetSummary(seg, summary); + ShieldCover(arena, seg); +} + +/* ArenaRead -- like ArenaPeek, but reference known to be owned by arena */ + +Ref ArenaRead(Arena arena, Ref *p) +{ + Bool b; + Seg seg = NULL; /* suppress "may be used uninitialized" */ + + AVERT(Arena, arena); + + b = SegOfAddr(&seg, arena, (Addr)p); + AVER(b == TRUE); - /* get the possibly fixed reference */ return ArenaPeekSeg(arena, seg, p); } +/* ArenaWrite -- like ArenaPoke, but reference known to be owned by arena */ + +void ArenaWrite(Arena arena, Ref *p, Ref ref) +{ + Bool b; + Seg seg = NULL; /* suppress "may be used uninitialized" */ + + AVERT(Arena, arena); + + b = SegOfAddr(&seg, arena, (Addr)p); + AVER(b == TRUE); + + ArenaPokeSeg(arena, seg, p, ref); +} + /* GlobalsDescribe -- describe the arena globals */ @@ -964,7 +1004,6 @@ Res GlobalsDescribe(Globals arenaGlobals, mps_lib_FILE *stream, Count depth) Res res; Arena arena; Ring node, nextNode; - Index i; TraceId ti; Trace trace; @@ -973,8 +1012,12 @@ Res GlobalsDescribe(Globals arenaGlobals, mps_lib_FILE *stream, Count depth) if (stream == NULL) return ResFAIL; + res = WriteF(stream, depth, "Globals\n", NULL); + if (res != ResOK) + return res; + arena = GlobalsArena(arenaGlobals); - res = WriteF(stream, depth, + res = WriteF(stream, depth + 2, "mpsVersion $S\n", (WriteFS)arenaGlobals->mpsVersionString, "lock $P\n", (WriteFP)arenaGlobals->lock, "pollThreshold $U kB\n", @@ -995,70 +1038,55 @@ Res GlobalsDescribe(Globals arenaGlobals, mps_lib_FILE *stream, Count depth) "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; - for(i=0; i < LDHistoryLENGTH; ++ i) { - res = WriteF(stream, depth + 2, - "[$U] = $B\n", (WriteFU)i, (WriteFB)arena->history[i], - NULL); - if (res != ResOK) - return res; - } - - 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); + res = HistoryDescribe(ArenaHistory(arena), stream, depth + 2); if (res != ResOK) return res; - res = RootsDescribe(arenaGlobals, stream, depth); + res = ShieldDescribe(ArenaShield(arena), stream, depth + 2); + if (res != ResOK) + return res; + + res = RootsDescribe(arenaGlobals, stream, depth + 2); if (res != ResOK) return res; RING_FOR(node, &arenaGlobals->poolRing, nextNode) { Pool pool = RING_ELT(Pool, arenaRing, node); - res = PoolDescribe(pool, stream, depth); + res = PoolDescribe(pool, stream, depth + 2); if (res != ResOK) return res; } RING_FOR(node, &arena->formatRing, nextNode) { Format format = RING_ELT(Format, arenaRing, node); - res = FormatDescribe(format, stream, depth); + res = FormatDescribe(format, stream, depth + 2); if (res != ResOK) return res; } RING_FOR(node, &arena->threadRing, nextNode) { Thread thread = ThreadRingThread(node); - res = ThreadDescribe(thread, stream, depth); + res = ThreadDescribe(thread, stream, depth + 2); if (res != ResOK) return res; } RING_FOR(node, &arena->chainRing, nextNode) { Chain chain = RING_ELT(Chain, chainRing, node); - res = ChainDescribe(chain, stream, depth); + res = ChainDescribe(chain, stream, depth + 2); if (res != ResOK) return res; } TRACE_SET_ITER(ti, trace, TraceSetUNIV, arena) if (TraceSetIsMember(arena->busyTraces, trace)) { - res = TraceDescribe(trace, stream, depth); + res = TraceDescribe(trace, stream, depth + 2); if (res != ResOK) return res; } @@ -1103,7 +1131,7 @@ Bool ArenaEmergency(Arena arena) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/land.c b/mps/code/land.c index 18d446288f8..18f2232f841 100644 --- a/mps/code/land.c +++ b/mps/code/land.c @@ -1,9 +1,16 @@ /* land.c: LAND (COLLECTION OF ADDRESS RANGES) IMPLEMENTATION * * $Id$ - * Copyright (c) 2014-2015 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2014-2016 Ravenbrook Limited. See end of file for license. * * .design: + * + * .critical.macros: In manual-allocation-bound programs using MVFF, + * the Land generic functions are on the critical path via mps_free. + * In non-checking varieties we provide macro alternatives (in mpm.h) + * to these functions that call the underlying methods directly, + * giving a few percent improvement in performance but skipping the + * re-entrancy checking provided by landEnter and landLeave. */ #include "mpm.h" @@ -12,6 +19,12 @@ SRCID(land, "$Id$"); +/* Forward declarations */ + +static Res landNoInsert(Range rangeReturn, Land land, Range range); +static Res landNoDelete(Range rangeReturn, Land land, Range range); + + /* FindDeleteCheck -- check method for a FindDelete value */ Bool FindDeleteCheck(FindDelete findDelete) @@ -41,7 +54,6 @@ 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) @@ -49,7 +61,6 @@ static void landLeave(Land land) /* Don't need to check as always called from interface function. */ AVER(land->inLand); land->inLand = FALSE; - return; } @@ -57,102 +68,68 @@ static void landLeave(Land land) Bool LandCheck(Land land) { + LandClass klass; /* .enter-leave.simple */ CHECKS(Land, land); - CHECKD(LandClass, land->class); + CHECKC(Land, land); + klass = ClassOfPoly(Land, land); + CHECKD(LandClass, klass); CHECKU(Arena, land->arena); CHECKL(AlignCheck(land->alignment)); CHECKL(BoolCheck(land->inLand)); return TRUE; } +static Res LandAbsInit(Land land, Arena arena, Align alignment, ArgList args) +{ + AVER(land != NULL); + AVERT(Arena, arena); + AVERT(Align, alignment); + UNUSED(args); + + /* Superclass init */ + InstInit(CouldBeA(Inst, land)); + + land->inLand = TRUE; + land->alignment = alignment; + land->arena = arena; + + SetClassOfPoly(land, CLASS(Land)); + land->sig = LandSig; + AVERC(Land, land); + + return ResOK; +} + +static void LandAbsFinish(Inst inst) +{ + Land land = MustBeA(Land, inst); + AVERC(Land, land); + land->sig = SigInvalid; + NextMethod(Inst, Land, finish)(inst); +} + /* LandInit -- initialize land * * See */ -Res LandInit(Land land, LandClass class, Arena arena, Align alignment, void *owner, ArgList args) +Res LandInit(Land land, LandClass klass, Arena arena, Align alignment, void *owner, ArgList args) { Res res; AVER(land != NULL); - AVERT(LandClass, class); + AVERT(LandClass, klass); 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); + res = klass->init(land, arena, alignment, args); if (res != ResOK) - goto failInit; + return res; 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); } @@ -163,12 +140,10 @@ void LandDestroy(Land land) void LandFinish(Land land) { - AVERT(Land, land); + AVERC(Land, land); landEnter(land); - (*land->class->finish)(land); - - land->sig = SigInvalid; + Method(Inst, land, finish)(MustBeA(Inst, land)); } @@ -177,12 +152,12 @@ void LandFinish(Land land) * See */ -Size LandSize(Land land) +Size (LandSize)(Land land) { /* .enter-leave.simple */ - AVERT(Land, land); + AVERC(Land, land); - return (*land->class->sizeMethod)(land); + return LandSizeMacro(land); } @@ -191,17 +166,18 @@ Size LandSize(Land land) * See */ -Res LandInsert(Range rangeReturn, Land land, Range range) +Res (LandInsert)(Range rangeReturn, Land land, Range range) { Res res; AVER(rangeReturn != NULL); - AVERT(Land, land); + AVERC(Land, land); AVERT(Range, range); AVER(RangeIsAligned(range, land->alignment)); + AVER(!RangeIsEmpty(range)); landEnter(land); - res = (*land->class->insert)(rangeReturn, land, range); + res = LandInsertMacro(rangeReturn, land, range); landLeave(land); return res; @@ -213,17 +189,17 @@ Res LandInsert(Range rangeReturn, Land land, Range range) * See */ -Res LandDelete(Range rangeReturn, Land land, Range range) +Res (LandDelete)(Range rangeReturn, Land land, Range range) { Res res; AVER(rangeReturn != NULL); - AVERT(Land, land); + AVERC(Land, land); AVERT(Range, range); AVER(RangeIsAligned(range, land->alignment)); landEnter(land); - res = (*land->class->delete)(rangeReturn, land, range); + res = LandDeleteMacro(rangeReturn, land, range); landLeave(land); return res; @@ -235,14 +211,14 @@ Res LandDelete(Range rangeReturn, Land land, Range range) * See */ -Bool LandIterate(Land land, LandVisitor visitor, void *closureP, Size closureS) +Bool (LandIterate)(Land land, LandVisitor visitor, void *closure) { Bool b; - AVERT(Land, land); + AVERC(Land, land); AVER(FUNCHECK(visitor)); landEnter(land); - b = (*land->class->iterate)(land, visitor, closureP, closureS); + b = LandIterateMacro(land, visitor, closure); landLeave(land); return b; @@ -255,14 +231,14 @@ Bool LandIterate(Land land, LandVisitor visitor, void *closureP, Size closureS) * See */ -Bool LandIterateAndDelete(Land land, LandDeleteVisitor visitor, void *closureP, Size closureS) +Bool (LandIterateAndDelete)(Land land, LandDeleteVisitor visitor, void *closure) { Bool b; - AVERT(Land, land); + AVERC(Land, land); AVER(FUNCHECK(visitor)); landEnter(land); - b = (*land->class->iterateAndDelete)(land, visitor, closureP, closureS); + b = LandIterateAndDeleteMacro(land, visitor, closure); landLeave(land); return b; @@ -274,19 +250,18 @@ Bool LandIterateAndDelete(Land land, LandDeleteVisitor visitor, void *closureP, * See */ -Bool LandFindFirst(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) +Bool (LandFindFirst)(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) { Bool b; AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); - AVERT(Land, land); + AVERC(Land, land); AVER(SizeIsAligned(size, land->alignment)); AVERT(FindDelete, findDelete); landEnter(land); - b = (*land->class->findFirst)(rangeReturn, oldRangeReturn, land, size, - findDelete); + b = LandFindFirstMacro(rangeReturn, oldRangeReturn, land, size, findDelete); landLeave(land); return b; @@ -298,19 +273,18 @@ Bool LandFindFirst(Range rangeReturn, Range oldRangeReturn, Land land, Size size * See */ -Bool LandFindLast(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) +Bool (LandFindLast)(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) { Bool b; AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); - AVERT(Land, land); + AVERC(Land, land); AVER(SizeIsAligned(size, land->alignment)); AVERT(FindDelete, findDelete); landEnter(land); - b = (*land->class->findLast)(rangeReturn, oldRangeReturn, land, size, - findDelete); + b = LandFindLastMacro(rangeReturn, oldRangeReturn, land, size, findDelete); landLeave(land); return b; @@ -322,19 +296,18 @@ Bool LandFindLast(Range rangeReturn, Range oldRangeReturn, Land land, Size size, * See */ -Bool LandFindLargest(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) +Bool (LandFindLargest)(Range rangeReturn, Range oldRangeReturn, Land land, Size size, FindDelete findDelete) { Bool b; AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); - AVERT(Land, land); + AVERC(Land, land); AVER(SizeIsAligned(size, land->alignment)); AVERT(FindDelete, findDelete); landEnter(land); - b = (*land->class->findLargest)(rangeReturn, oldRangeReturn, land, size, - findDelete); + b = LandFindLargestMacro(rangeReturn, oldRangeReturn, land, size, findDelete); landLeave(land); return b; @@ -346,21 +319,21 @@ Bool LandFindLargest(Range rangeReturn, Range oldRangeReturn, Land land, Size si * See */ -Res LandFindInZones(Bool *foundReturn, Range rangeReturn, Range oldRangeReturn, Land land, Size size, ZoneSet zoneSet, Bool high) +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); + AVERC(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); + res = LandFindInZonesMacro(foundReturn, rangeReturn, oldRangeReturn, + land, size, zoneSet, high); landLeave(land); return res; @@ -374,53 +347,33 @@ Res LandFindInZones(Bool *foundReturn, Range rangeReturn, Range oldRangeReturn, 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; + return Method(Inst, land, describe)(MustBeA(Inst, land), stream, depth); } /* landFlushVisitor -- visitor for LandFlush. * - * closureP argument is the destination Land. Attempt to insert the + * closure argument is the destination Land. Attempt to insert the * range into the destination. + * + * .flush.critical: In manual-allocation-bound programs using MVFF + * this is on the critical paths via mps_alloc (and then PoolAlloc, + * MVFFAlloc, failoverFind*, LandFlush) and mps_free (and then + * MVFFFree, failoverInsert, LandFlush). */ -static Bool landFlushVisitor(Bool *deleteReturn, Land land, Range range, - void *closureP, Size closureS) +Bool LandFlushVisitor(Bool *deleteReturn, Land land, Range range, + void *closure) { Res res; RangeStruct newRange; Land dest; - AVER(deleteReturn != NULL); - AVERT(Land, land); - AVERT(Range, range); - AVER(closureP != NULL); - AVER(closureS == UNUSED_SIZE); - UNUSED(closureS); + AVER_CRITICAL(deleteReturn != NULL); + AVERC_CRITICAL(Land, land); + AVERT_CRITICAL(Range, range); + AVER_CRITICAL(closure != NULL); - dest = closureP; + dest = MustBeA_CRITICAL(Land, closure); res = LandInsert(&newRange, dest, range); if (res == ResOK) { *deleteReturn = TRUE; @@ -437,50 +390,39 @@ static Bool landFlushVisitor(Bool *deleteReturn, Land land, Range range, * See */ -Bool LandFlush(Land dest, Land src) +Bool (LandFlush)(Land dest, Land src) { - AVERT(Land, dest); - AVERT(Land, src); + AVERC(Land, dest); + AVERC(Land, src); - return LandIterateAndDelete(src, landFlushVisitor, dest, UNUSED_SIZE); + return LandFlushMacro(dest, src); } /* LandClassCheck -- check land class */ -Bool LandClassCheck(LandClass class) +Bool LandClassCheck(LandClass klass) { - 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); + CHECKL(InstClassCheck(&klass->instClassStruct)); + CHECKL(klass->size >= sizeof(LandStruct)); + CHECKL(FUNCHECK(klass->init)); + CHECKL(FUNCHECK(klass->insert)); + CHECKL(FUNCHECK(klass->delete)); + CHECKL(FUNCHECK(klass->findFirst)); + CHECKL(FUNCHECK(klass->findLast)); + CHECKL(FUNCHECK(klass->findLargest)); + CHECKL(FUNCHECK(klass->findInZones)); + + /* Check that land classes override sets of related methods. */ + CHECKL((klass->init == LandAbsInit) + == (klass->instClassStruct.finish == LandAbsFinish)); + CHECKL((klass->insert == landNoInsert) == (klass->delete == landNoDelete)); + + CHECKS(LandClass, klass); 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); @@ -491,17 +433,15 @@ static Size landNoSize(Land land) /* LandSlowSize -- generic size method but slow */ static Bool landSizeVisitor(Land land, Range range, - void *closureP, Size closureS) + void *closure) { Size *size; - AVERT(Land, land); + AVERC(Land, land); AVERT(Range, range); - AVER(closureP != NULL); - AVER(closureS == UNUSED_SIZE); - UNUSED(closureS); + AVER(closure != NULL); - size = closureP; + size = closure; *size += RangeSize(range); return TRUE; @@ -510,7 +450,7 @@ static Bool landSizeVisitor(Land land, Range range, Size LandSlowSize(Land land) { Size size = 0; - Bool b = LandIterate(land, landSizeVisitor, &size, UNUSED_SIZE); + Bool b = LandIterate(land, landSizeVisitor, &size); AVER(b); return size; } @@ -518,7 +458,7 @@ Size LandSlowSize(Land land) static Res landNoInsert(Range rangeReturn, Land land, Range range) { AVER(rangeReturn != NULL); - AVERT(Land, land); + AVERC(Land, land); AVERT(Range, range); return ResUNIMPL; } @@ -526,26 +466,24 @@ static Res landNoInsert(Range rangeReturn, Land land, Range range) static Res landNoDelete(Range rangeReturn, Land land, Range range) { AVER(rangeReturn != NULL); - AVERT(Land, land); + AVERC(Land, land); AVERT(Range, range); return ResUNIMPL; } -static Bool landNoIterate(Land land, LandVisitor visitor, void *closureP, Size closureS) +static Bool landNoIterate(Land land, LandVisitor visitor, void *closure) { - AVERT(Land, land); + AVERC(Land, land); AVER(visitor != NULL); - UNUSED(closureP); - UNUSED(closureS); + UNUSED(closure); return FALSE; } -static Bool landNoIterateAndDelete(Land land, LandDeleteVisitor visitor, void *closureP, Size closureS) +static Bool landNoIterateAndDelete(Land land, LandDeleteVisitor visitor, void *closure) { - AVERT(Land, land); + AVERC(Land, land); AVER(visitor != NULL); - UNUSED(closureP); - UNUSED(closureS); + UNUSED(closure); return FALSE; } @@ -553,7 +491,7 @@ static Bool landNoFind(Range rangeReturn, Range oldRangeReturn, Land land, Size { AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); - AVERT(Land, land); + AVERC(Land, land); UNUSED(size); AVERT(FindDelete, findDelete); return ResUNIMPL; @@ -564,49 +502,68 @@ static Res landNoFindInZones(Bool *foundReturn, Range rangeReturn, Range oldRang AVER(foundReturn != NULL); AVER(rangeReturn != NULL); AVER(oldRangeReturn != NULL); - AVERT(Land, land); + AVERC(Land, land); UNUSED(size); UNUSED(zoneSet); AVERT(Bool, high); return ResUNIMPL; } -static Res landTrivDescribe(Land land, mps_lib_FILE *stream, Count depth) +static Res LandAbsDescribe(Inst inst, mps_lib_FILE *stream, Count depth) { - if (!TESTT(Land, land)) - return ResFAIL; + Land land = CouldBeA(Land, inst); + LandClass klass; + Res res; + + if (!TESTC(Land, land)) + return ResPARAM; if (stream == NULL) - return ResFAIL; - UNUSED(depth); - /* dispatching function does it all */ - return ResOK; + return ResPARAM; + + res = NextMethod(Inst, Land, describe)(inst, stream, depth); + if (res != ResOK) + return res; + + klass = ClassOfPoly(Land, land); + return WriteF(stream, depth + 2, + "class $P (\"$S\")\n", + (WriteFP)klass, (WriteFS)ClassName(klass), + "arena $P\n", (WriteFP)land->arena, + "align $U\n", (WriteFU)land->alignment, + "inLand $S\n", WriteFYesNo(land->inLand), + NULL); } -DEFINE_CLASS(LandClass, class) +DEFINE_CLASS(Inst, LandClass, klass) { - 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); + INHERIT_CLASS(klass, LandClass, InstClass); + AVERT(InstClass, klass); +} + +DEFINE_CLASS(Land, Land, klass) +{ + INHERIT_CLASS(&klass->instClassStruct, Land, Inst); + klass->instClassStruct.describe = LandAbsDescribe; + klass->instClassStruct.finish = LandAbsFinish; + klass->size = sizeof(LandStruct); + klass->init = LandAbsInit; + klass->sizeMethod = landNoSize; + klass->insert = landNoInsert; + klass->delete = landNoDelete; + klass->iterate = landNoIterate; + klass->iterateAndDelete = landNoIterateAndDelete; + klass->findFirst = landNoFind; + klass->findLast = landNoFind; + klass->findLargest = landNoFind; + klass->findInZones = landNoFindInZones; + klass->sig = LandClassSig; + AVERT(LandClass, klass); } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2014-2015 Ravenbrook Limited . + * Copyright (C) 2014-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/landtest.c b/mps/code/landtest.c index eb4229f65e8..53a04cc98e7 100644 --- a/mps/code/landtest.c +++ b/mps/code/landtest.c @@ -1,7 +1,7 @@ /* landtest.c: LAND TEST * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * * Test all three Land implementations against duplicate operations on * a bit-table. @@ -62,18 +62,18 @@ static Index (indexOfAddr)(TestState state, Addr a) } -static void describe(TestState state) { +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) +static Bool checkVisitor(Land land, Range range, void *closure) { Addr base, limit; - CheckTestClosure cl = closureP; + CheckTestClosure cl = closure; testlib_unused(land); - Insist(closureS == UNUSED_SIZE); Insist(cl != NULL); base = RangeBase(range); @@ -106,7 +106,7 @@ static void check(TestState state) closure.limit = addrOfIndex(state, state->size); closure.oldLimit = state->block; - b = LandIterate(state->land, checkVisitor, &closure, UNUSED_SIZE); + b = LandIterate(state->land, checkVisitor, &closure); Insist(b); if (closure.oldLimit == state->block) @@ -385,11 +385,10 @@ static void find(TestState state, Size size, Bool high, FindDelete findDelete) BTSetRange(state->allocTable, expectedBase, expectedLimit); } } - - return; } -static void test(TestState state, unsigned n) { +static void test(TestState state, unsigned n, unsigned operations) +{ Addr base, limit; unsigned i; Size size; @@ -399,7 +398,7 @@ static void test(TestState state, unsigned n) { BTSetRange(state->allocTable, 0, state->size); /* Initially all allocated */ check(state); for(i = 0; i < n; i++) { - switch(fbmRnd(3)) { + switch (fbmRnd(operations)) { case 0: randomRange(&base, &limit, state); allocate(state, base, limit); @@ -420,7 +419,7 @@ static void test(TestState state, unsigned n) { find(state, size, high, findDelete); break; default: - cdie(0, "invalid rnd(3)"); + cdie(0, "invalid operation"); return; } if ((i + 1) % 1000 == 0) @@ -430,8 +429,16 @@ static void test(TestState state, unsigned n) { #define testArenaSIZE (((size_t)4)<<20) -extern int main(int argc, char *argv[]) +int main(int argc, char *argv[]) { + static const struct { + LandClass (*klass)(void); + unsigned operations; + } cbsConfig[] = { + {CBSClassGet, 2}, + {CBSFastClassGet, 3}, + {CBSZonedClassGet, 3}, + }; mps_arena_t mpsArena; Arena arena; TestStateStruct state; @@ -444,7 +451,7 @@ extern int main(int argc, char *argv[]) Land fl = FreelistLand(&flStruct); Land fo = FailoverLand(&foStruct); Pool mfs = MFSPool(&blockPool); - int i; + size_t i; testlib_init(argc, argv); state.size = ArraySize; @@ -460,8 +467,7 @@ extern int main(int argc, char *argv[]) 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), + die((mps_res_t)ControlAlloc(&p, arena, (state.size + 1) * state.align), "failed to allocate block"); state.block = AddrAlignUp(p, state.align); @@ -472,22 +478,24 @@ extern int main(int argc, char *argv[]) /* 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); + for (i = 0; i < NELEMS(cbsConfig); ++i) { + MPS_ARGS_BEGIN(args) { + die((mps_res_t)LandInit(cbs, cbsConfig[i].klass(), arena, state.align, + NULL, args), + "failed to initialise CBS"); + } MPS_ARGS_END(args); + state.land = cbs; + test(&state, nCBSOperations, cbsConfig[i].operations); + LandFinish(cbs); + } /* 2. Test Freelist */ - die((mps_res_t)LandInit(fl, FreelistLandClassGet(), arena, state.align, + die((mps_res_t)LandInit(fl, CLASS(Freelist), arena, state.align, NULL, mps_args_none), "failed to initialise Freelist"); state.land = fl; - test(&state, nFLOperations); + test(&state, nFLOperations, 3); LandFinish(fl); /* 3. Test CBS-failing-over-to-Freelist (always failing over on @@ -499,30 +507,30 @@ extern int main(int argc, char *argv[]) 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); + MPS_ARGS_ADD(piArgs, MFSExtendSelf, i != 0); 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, + die((mps_res_t)LandInit(cbs, CLASS(CBSFast), arena, state.align, NULL, args), "failed to initialise CBS"); } MPS_ARGS_END(args); - die((mps_res_t)LandInit(fl, FreelistLandClassGet(), arena, state.align, + die((mps_res_t)LandInit(fl, CLASS(Freelist), 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, + die((mps_res_t)LandInit(fo, CLASS(Failover), arena, state.align, NULL, args), "failed to initialise Failover"); } MPS_ARGS_END(args); state.land = fo; - test(&state, nFOOperations); + test(&state, nFOOperations, 3); LandFinish(fo); LandFinish(fl); LandFinish(cbs); @@ -547,7 +555,7 @@ extern int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2014 Ravenbrook Limited . + * Copyright (c) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/ld.c b/mps/code/ld.c index c9e79f8a762..71264ff78a7 100644 --- a/mps/code/ld.c +++ b/mps/code/ld.c @@ -51,6 +51,88 @@ SRCID(ld, "$Id$"); +void HistoryInit(History history) +{ + Index i; + + AVER(history != NULL); + + history->epoch = 0; + history->prehistory = RefSetEMPTY; + for (i = 0; i < LDHistoryLENGTH; ++i) + history->history[i] = RefSetEMPTY; + + history->sig = HistorySig; + AVERT(History, history); +} + +Bool HistoryCheck(History history) +{ + Index i; + RefSet rs; + + CHECKS(History, history); + + /* check that each history entry is a subset of the next oldest */ + rs = RefSetEMPTY; + /* note this loop starts from 1; there is no history age 0 */ + for (i = 1; i <= LDHistoryLENGTH; ++i) { + /* check history age 'i'; 'j' is the history index. */ + Index j = (history->epoch + LDHistoryLENGTH - i) % LDHistoryLENGTH; + CHECKL(RefSetSub(rs, history->history[j])); + rs = history->history[j]; + } + /* the oldest history entry must be a subset of the prehistory */ + CHECKL(RefSetSub(rs, history->prehistory)); + + return TRUE; +} + +void HistoryFinish(History history) +{ + AVERT(History, history); + history->sig = SigInvalid; +} + +Res HistoryDescribe(History history, mps_lib_FILE *stream, Count depth) +{ + Res res; + Index i; + + if (!TESTT(History, history)) + return ResPARAM; + if (stream == NULL) + return ResPARAM; + + res = WriteF(stream, depth, + "History $P {\n", (WriteFP)history, + " epoch = $U\n", (WriteFU)history->epoch, + " prehistory = $B\n", (WriteFB)history->prehistory, + " history {\n", + " [note: indices are raw, not rotated]\n", + NULL); + if (res != ResOK) + return res; + + for (i = 0; i < LDHistoryLENGTH; ++i) { + res = WriteF(stream, depth + 4, + "[$U] = $B\n", (WriteFU)i, (WriteFB)history->history[i], + NULL); + if (res != ResOK) + return res; + } + + res = WriteF(stream, depth, + " }\n", + "} History $P\n", (WriteFP)history, + NULL); + if (res != ResOK) + return res; + + return ResOK; +} + + /* LDReset -- reset a dependency to empty * * .reset.sync: This does not need to be synchronized with LDAge @@ -68,7 +150,7 @@ void LDReset(mps_ld_t ld, Arena arena) b = SegOfAddr(&seg, arena, (Addr)ld); if (b) ShieldExpose(arena, seg); /* .ld.access */ - ld->_epoch = arena->epoch; + ld->_epoch = ArenaHistory(arena)->epoch; ld->_rs = RefSetEMPTY; if (b) ShieldCover(arena, seg); @@ -106,7 +188,7 @@ 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); + AVER(ld->_epoch <= ArenaHistory(arena)->epoch); ld->_rs = RefSetAdd(arena, ld->_rs, addr); } @@ -134,23 +216,25 @@ void LDAdd(mps_ld_t ld, Arena arena, Addr addr) */ Bool LDIsStaleAny(mps_ld_t ld, Arena arena) { + History history; RefSet rs; AVER(ld != NULL); AVER(TESTT(Arena, arena)); /* .stale.thread-safe */ - AVER(ld->_epoch <= arena->epoch); + history = ArenaHistory(arena); + AVER(ld->_epoch <= history->epoch); - if (arena->epoch == ld->_epoch) /* .stale.current */ + if (history->epoch == ld->_epoch) /* .stale.current */ return FALSE; /* Load the history refset, _then_ check to see if it's recent. * This may in fact load an okay refset, which we decide to throw * away and use the pre-history instead. */ - rs = arena->history[ld->_epoch % LDHistoryLENGTH]; + rs = history->history[ld->_epoch % LDHistoryLENGTH]; /* .stale.recent */ /* .stale.recent.conservative */ - if (arena->epoch - ld->_epoch > LDHistoryLENGTH) { - rs = arena->prehistory; /* .stale.old */ + if (history->epoch - ld->_epoch > LDHistoryLENGTH) { + rs = history->prehistory; /* .stale.old */ } return RefSetInter(ld->_rs, rs) != RefSetEMPTY; @@ -186,28 +270,30 @@ Bool LDIsStale(mps_ld_t ld, Arena arena, Addr addr) */ void LDAge(Arena arena, RefSet rs) { + History history; Size i; AVERT(Arena, arena); + history = ArenaHistory(arena); AVER(rs != RefSetEMPTY); /* Replace the entry for epoch - LDHistoryLENGTH by an empty */ /* set which will become the set which has moved since the */ /* current epoch. */ - arena->history[arena->epoch % LDHistoryLENGTH] = RefSetEMPTY; + history->history[history->epoch % LDHistoryLENGTH] = RefSetEMPTY; /* Record the fact that the moved set has moved, by adding it */ /* to all the sets in the history, including the set for the */ /* current epoch. */ for(i = 0; i < LDHistoryLENGTH; ++i) - arena->history[i] = RefSetUnion(arena->history[i], rs); + history->history[i] = RefSetUnion(history->history[i], rs); /* This is the union of all movement since time zero. */ - arena->prehistory = RefSetUnion(arena->prehistory, rs); + history->prehistory = RefSetUnion(history->prehistory, rs); /* Advance the epoch by one. */ - ++arena->epoch; - AVER(arena->epoch != 0); /* .epoch-size */ + ++history->epoch; + AVER(history->epoch != 0); /* .epoch-size */ } @@ -221,9 +307,9 @@ void LDMerge(mps_ld_t ld, Arena arena, mps_ld_t from) { AVER(ld != NULL); AVER(TESTT(Arena, arena)); /* .merge.lock-free */ - AVER(ld->_epoch <= arena->epoch); + AVER(ld->_epoch <= ArenaHistory(arena)->epoch); AVER(from != NULL); - AVER(from->_epoch <= arena->epoch); + AVER(from->_epoch <= ArenaHistory(arena)->epoch); /* If a reference has been added since epoch e1 then I've */ /* certainly added since epoch e0 where e0 < e1. Therefore */ diff --git a/mps/code/lii3gc.gmk b/mps/code/lii3gc.gmk index 00be40c673c..2795633655f 100644 --- a/mps/code/lii3gc.gmk +++ b/mps/code/lii3gc.gmk @@ -3,19 +3,19 @@ # lii3gc.gmk: BUILD FOR LINUX/x86/GCC PLATFORM # # $Id$ -# Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. PFM = lii3gc MPMPF = \ - lockli.c \ - prmci3li.c \ - proti3.c \ + lockix.c \ + prmci3.c \ + prmcix.c \ + prmclii3.c \ protix.c \ - protli.c \ + protsgix.c \ pthrdext.c \ span.c \ - ssixi3.c \ thix.c \ vmix.c @@ -27,7 +27,7 @@ include comm.gmk # C. COPYRIGHT AND LICENSE # -# Copyright (C) 2001-2014 Ravenbrook Limited . +# Copyright (C) 2001-2016 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. # diff --git a/mps/code/lii6gc.gmk b/mps/code/lii6gc.gmk index 91f8f5d9066..dd04715299f 100644 --- a/mps/code/lii6gc.gmk +++ b/mps/code/lii6gc.gmk @@ -3,19 +3,19 @@ # lii6gc.gmk: BUILD FOR LINUX/x64/GCC PLATFORM # # $Id$ -# Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. PFM = lii6gc MPMPF = \ - lockli.c \ - prmci6li.c \ - proti6.c \ + lockix.c \ + prmci6.c \ + prmcix.c \ + prmclii6.c \ protix.c \ - protli.c \ + protsgix.c \ pthrdext.c \ span.c \ - ssixi6.c \ thix.c \ vmix.c @@ -27,7 +27,7 @@ include comm.gmk # C. COPYRIGHT AND LICENSE # -# Copyright (C) 2001-2014 Ravenbrook Limited . +# Copyright (C) 2001-2016 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. # diff --git a/mps/code/lii6ll.gmk b/mps/code/lii6ll.gmk index 5988b0c0b17..048ccb9f242 100644 --- a/mps/code/lii6ll.gmk +++ b/mps/code/lii6ll.gmk @@ -3,19 +3,19 @@ # lii6ll.gmk: BUILD FOR LINUX/x64/Clang PLATFORM # # $Id$ -# Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. PFM = lii6ll MPMPF = \ - lockli.c \ - prmci6li.c \ - proti6.c \ + lockix.c \ + prmci6.c \ + prmcix.c \ + prmclii6.c \ protix.c \ - protli.c \ + protsgix.c \ pthrdext.c \ span.c \ - ssixi6.c \ thix.c \ vmix.c @@ -27,7 +27,7 @@ include comm.gmk # C. COPYRIGHT AND LICENSE # -# Copyright (C) 2001-2014 Ravenbrook Limited . +# Copyright (C) 2001-2016 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. # diff --git a/mps/code/ll.gmk b/mps/code/ll.gmk index 787380fb3ed..db30cde45f0 100644 --- a/mps/code/ll.gmk +++ b/mps/code/ll.gmk @@ -13,7 +13,6 @@ CC = clang CFLAGSDEBUG = -O0 -g3 CFLAGSOPT = -O2 -g3 CFLAGSCOMPILER := \ - -pedantic \ -Waggregate-return \ -Wall \ -Wcast-qual \ @@ -32,7 +31,7 @@ CFLAGSCOMPILER := \ -Wstrict-prototypes \ -Wunreachable-code \ -Wwrite-strings -CFLAGSCOMPILERSTRICT := +CFLAGSCOMPILERSTRICT := -std=c89 -pedantic # A different set of compiler flags for less strict compilation, for # instance when we need to #include a third-party header file that diff --git a/mps/code/lock.h b/mps/code/lock.h index 35d0ed6db41..32664d2262a 100644 --- a/mps/code/lock.h +++ b/mps/code/lock.h @@ -21,6 +21,11 @@ extern size_t LockSize(void); +/* LockInitGlobal -- initialize global locks */ + +extern void LockInitGlobal(void); + + /* LockInit/Finish * * lock points to the allocated lock structure. A lock has no @@ -78,6 +83,11 @@ extern void LockRelease(Lock lock); extern Bool LockCheck(Lock lock); +/* LockIsHeld -- test whether lock is held by any thread */ + +extern Bool LockIsHeld(Lock lock); + + /* == Global locks == */ @@ -123,24 +133,9 @@ extern void LockClaimGlobal(void); extern void LockReleaseGlobal(void); -#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 LockRelease(lock) UNUSED(lock) -#define LockCheck(lock) ((void)lock, TRUE) -#define LockClaimGlobalRecursive() -#define LockReleaseGlobalRecursive() -#define LockClaimGlobal() -#define LockReleaseGlobal() -#else -#error "No lock configuration." -#endif /* LOCK */ +/* LockSetup -- one-time lock initialization */ + +extern void LockSetup(void); #endif /* lock_h */ diff --git a/mps/code/lockan.c b/mps/code/lockan.c index fe5082a6ebf..061b63d20a9 100644 --- a/mps/code/lockan.c +++ b/mps/code/lockan.c @@ -79,6 +79,12 @@ void (LockReleaseRecursive)(Lock lock) --lock->claims; } +Bool (LockIsHeld)(Lock lock) +{ + AVERT(Lock, lock); + return lock->claims > 0; +} + /* Global locking is performed by normal locks. * A separate lock structure is used for recursive and @@ -100,6 +106,13 @@ static Lock globalLock = &globalLockStruct; static Lock globalRecLock = &globalRecursiveLockStruct; +void LockInitGlobal(void) +{ + globalLock->claims = 0; + LockInit(globalLock); + globalRecLock->claims = 0; + LockInit(globalRecLock); +} void (LockClaimGlobalRecursive)(void) { @@ -121,6 +134,11 @@ void (LockReleaseGlobal)(void) LockRelease(globalLock); } +void LockSetup(void) +{ + /* Nothing to do as ANSI platform does not have fork(). */ +} + /* C. COPYRIGHT AND LICENSE * diff --git a/mps/code/lockcov.c b/mps/code/lockcov.c index 84866046d82..af225d07c40 100644 --- a/mps/code/lockcov.c +++ b/mps/code/lockcov.c @@ -39,20 +39,28 @@ int main(int argc, char *argv[]) Insist(b != NULL); LockInit(a); + Insist(!LockIsHeld(a)); LockInit(b); + Insist(!LockIsHeld(b)); LockClaimGlobal(); LockClaim(a); + Insist(LockIsHeld(a)); LockClaimRecursive(b); + Insist(LockIsHeld(b)); LockClaimGlobalRecursive(); LockReleaseGlobal(); LockClaimGlobal(); LockRelease(a); + Insist(!LockIsHeld(a)); LockClaimGlobalRecursive(); LockReleaseGlobal(); LockClaimRecursive(b); + Insist(LockIsHeld(b)); LockFinish(a); LockReleaseRecursive(b); + Insist(LockIsHeld(b)); LockReleaseRecursive(b); + Insist(!LockIsHeld(b)); LockFinish(b); LockInit(a); LockClaim(a); diff --git a/mps/code/lockix.c b/mps/code/lockix.c index c982bf0cb17..17aab7bd014 100644 --- a/mps/code/lockix.c +++ b/mps/code/lockix.c @@ -1,7 +1,7 @@ /* lockix.c: RECURSIVE LOCKS FOR POSIX SYSTEMS * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 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. @@ -9,7 +9,7 @@ * .freebsd: This implementation supports FreeBSD (platform * MPS_OS_FR). * - * .darwin: This implementation supports Darwin (OS X) (platform + * .darwin: This implementation supports Darwin (macOS) (platform * MPS_OS_XC). * * .design: These locks are implemented using mutexes. @@ -24,25 +24,26 @@ * number of claims acquired on a lock. This field must only be * modified while we hold the mutex. * - * .from: This version was copied from the FreeBSD version (lockfr.c) - * which was itself a cleaner version of the Linux version (lockli.c). + * .from: This was copied from the FreeBSD implementation (lockfr.c) + * which was itself a cleaner version of the LinuxThreads + * implementation (lockli.c). */ -#include +#include "mpm.h" + +#if !defined(MPS_OS_FR) && !defined(MPS_OS_LI) && !defined(MPS_OS_XC) +#error "lockix.c is specific to MPS_OS_FR, MPS_OS_LI or MPS_OS_XC" +#endif + +#include "lock.h" + +#include /* see .feature.li in config.h */ #include #include -#include "mpmtypes.h" -#include "lock.h" -#include "config.h" - - -#if !defined(MPS_OS_FR) && !defined(MPS_OS_XC) -#error "lockix.c is Unix specific, currently for MPS_OS_FR XC." -#endif - SRCID(lockix, "$Id$"); +#if defined(LOCK) /* LockStruct -- the MPS lock structure * @@ -122,7 +123,7 @@ void (LockClaim)(Lock lock) res = pthread_mutex_lock(&lock->mut); /* pthread_mutex_lock will error if we own the lock already. */ - AVER(res == 0); + AVER(res == 0); /* */ /* This should be the first claim. Now we own the mutex */ /* it is ok to check this. */ @@ -158,8 +159,8 @@ void (LockClaimRecursive)(Lock lock) /* pthread_mutex_lock will return: */ /* 0 if we have just claimed the lock */ /* EDEADLK if we own the lock already. */ - AVER((res == 0 && lock->claims == 0) || - (res == EDEADLK && lock->claims > 0)); + AVER((res == 0) == (lock->claims == 0)); + AVER((res == EDEADLK) == (lock->claims > 0)); ++lock->claims; AVER(lock->claims > 0); @@ -183,6 +184,21 @@ void (LockReleaseRecursive)(Lock lock) } +/* LockIsHeld -- test whether lock is held */ + +Bool (LockIsHeld)(Lock lock) +{ + AVERT(Lock, lock); + if (pthread_mutex_trylock(&lock->mut) == 0) { + Bool claimed = lock->claims > 0; + int res = pthread_mutex_unlock(&lock->mut); + AVER(res == 0); + return claimed; + } + return TRUE; +} + + /* Global locks * * .global: The two "global" locks are statically allocated normal locks. @@ -194,7 +210,7 @@ static Lock globalLock = &globalLockStruct; static Lock globalRecLock = &globalRecLockStruct; static pthread_once_t isGlobalLockInit = PTHREAD_ONCE_INIT; -static void globalLockInit(void) +void LockInitGlobal(void) { LockInit(globalLock); LockInit(globalRecLock); @@ -208,7 +224,7 @@ void (LockClaimGlobalRecursive)(void) int res; /* Ensure the global lock has been initialized */ - res = pthread_once(&isGlobalLockInit, globalLockInit); + res = pthread_once(&isGlobalLockInit, LockInitGlobal); AVER(res == 0); LockClaimRecursive(globalRecLock); } @@ -229,7 +245,7 @@ void (LockClaimGlobal)(void) int res; /* Ensure the global lock has been initialized */ - res = pthread_once(&isGlobalLockInit, globalLockInit); + res = pthread_once(&isGlobalLockInit, LockInitGlobal); AVER(res == 0); LockClaim(globalLock); } @@ -243,9 +259,26 @@ void (LockReleaseGlobal)(void) } +/* LockSetup -- one-time lock initialization */ + +void LockSetup(void) +{ + /* Claim all locks before a fork; release in the parent; + reinitialize in the child */ + pthread_atfork(GlobalsClaimAll, GlobalsReleaseAll, GlobalsReinitializeAll); +} + + +#elif defined(LOCK_NONE) +#include "lockan.c" +#else +#error "No lock configuration." +#endif + + /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/lockli.c b/mps/code/lockli.c deleted file mode 100644 index 0dc98fb8a25..00000000000 --- a/mps/code/lockli.c +++ /dev/null @@ -1,299 +0,0 @@ -/* lockli.c: RECURSIVE LOCKS FOR POSIX SYSTEMS - * - * $Id$ - * 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. - * - * .posix: In fact, the implementation should be reusable for most POSIX - * implementations, but may need some customization for each. - * - * .design: These locks are implemented using mutexes. - * - * .recursive: Mutexes support both non-recursive and recursive locking, but - * only at initialization time. This doesn't match the API of MPS Lock module, - * which chooses at locking time, so all locks are made (non-recursive) - * errorchecking. Recursive locks are implemented by checking the error - * code. - * - * .claims: During use the claims field is updated to remember the number of - * claims acquired on a lock. This field must only be modified - * while we hold the mutex. - */ - -#include "mpmtypes.h" -#include "lock.h" -#include "config.h" - -#include /* see .feature.li in config.h */ -#include -#include - - -#ifndef MPS_OS_LI -#error "lockli.c is specific to LinuxThreads but MPS_OS_LI not defined" -#endif - -SRCID(lockli, "$Id$"); - - -/* LockAttrSetRecursive -- Set mutexattr to permit recursive locking - * - * There's a standard way to do this - but early LinuxThreads doesn't - * quite follow the standard. Some other implementations might not - * either. - */ - -#ifdef OLD_LINUXTHREADS - -#define LockAttrSetRecursive(attrptr) \ - pthread_mutexattr_setkind_np(attrptr, PTHREAD_MUTEX_ERRORCHECK_NP) - -#else - -#define LockAttrSetRecursive(attrptr) \ - pthread_mutexattr_settype(attrptr, PTHREAD_MUTEX_ERRORCHECK) - -#endif - - -/* LockStruct -- the MPS lock structure - * - * .lock.posix: Posix lock structure; uses a mutex. - */ - -typedef struct LockStruct { - Sig sig; /* */ - unsigned long claims; /* # claims held by owner */ - pthread_mutex_t mut; /* the mutex itself */ -} LockStruct; - - -/* LockSize -- size of a LockStruct */ - -size_t (LockSize)(void) -{ - return sizeof(LockStruct); -} - - -/* LockCheck -- check a 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. */ - /* There's no way to test the mutex, or check if it's held by somebody. */ - return TRUE; -} - - -/* LockInit -- initialize a lock */ - -void (LockInit)(Lock lock) -{ - pthread_mutexattr_t attr; - int res; - - AVER(lock != NULL); - lock->claims = 0; - res = pthread_mutexattr_init(&attr); - AVER(res == 0); - res = LockAttrSetRecursive(&attr); - AVER(res == 0); - res = pthread_mutex_init(&lock->mut, &attr); - AVER(res == 0); - res = pthread_mutexattr_destroy(&attr); - AVER(res == 0); - lock->sig = LockSig; - AVERT(Lock, lock); -} - - -/* LockFinish -- finish a lock */ - -void (LockFinish)(Lock lock) -{ - int res; - - AVERT(Lock, lock); - /* Lock should not be finished while held */ - AVER(lock->claims == 0); - res = pthread_mutex_destroy(&lock->mut); - AVER(res == 0); - lock->sig = SigInvalid; -} - - -/* LockClaim -- claim a lock (non-recursive) */ - -void (LockClaim)(Lock lock) -{ - int res; - - AVERT(Lock, lock); - - res = pthread_mutex_lock(&lock->mut); - /* pthread_mutex_lock will error if we own the lock already. */ - AVER(res == 0); - - /* This should be the first claim. Now we own the mutex */ - /* it is ok to check this. */ - AVER(lock->claims == 0); - lock->claims = 1; -} - - -/* LockRelease -- release a lock (non-recursive) */ - -void (LockRelease)(Lock lock) -{ - int res; - - AVERT(Lock, lock); - AVER(lock->claims == 1); /* The lock should only be held once */ - lock->claims = 0; /* Must set this before releasing the lock */ - res = pthread_mutex_unlock(&lock->mut); - /* pthread_mutex_unlock will error if we didn't own the lock. */ - AVER(res == 0); -} - - -/* LockClaimRecursive -- claim a lock (recursive) */ - -void (LockClaimRecursive)(Lock lock) -{ - int res; - - AVERT(Lock, lock); - - res = pthread_mutex_lock(&lock->mut); - /* pthread_mutex_lock will return: */ - /* 0 if we have just claimed the lock */ - /* EDEADLK if we own the lock already. */ - AVER((res == 0 && lock->claims == 0) || - (res == EDEADLK && lock->claims > 0)); - - ++lock->claims; - AVER(lock->claims > 0); -} - - -/* LockReleaseRecursive -- release a lock (recursive) */ - -void (LockReleaseRecursive)(Lock lock) -{ - int res; - - AVERT(Lock, lock); - AVER(lock->claims > 0); - --lock->claims; - if (lock->claims == 0) { - res = pthread_mutex_unlock(&lock->mut); - /* pthread_mutex_unlock will error if we didn't own the lock. */ - AVER(res == 0); - } -} - - -/* Global locks - * - * .global: The two "global" locks are statically allocated normal locks. - */ - -static LockStruct globalLockStruct; -static LockStruct globalRecLockStruct; -static Lock globalLock = &globalLockStruct; -static Lock globalRecLock = &globalRecLockStruct; -static pthread_once_t isGlobalLockInit = PTHREAD_ONCE_INIT; - -static void globalLockInit(void) -{ - LockInit(globalLock); - LockInit(globalRecLock); -} - - -/* LockClaimGlobalRecursive -- claim the global recursive lock */ - -void (LockClaimGlobalRecursive)(void) -{ - int res; - - /* Ensure the global lock has been initialized */ - res = pthread_once(&isGlobalLockInit, globalLockInit); - AVER(res == 0); - LockClaimRecursive(globalRecLock); -} - - -/* LockReleaseGlobalRecursive -- release the global recursive lock */ - -void (LockReleaseGlobalRecursive)(void) -{ - LockReleaseRecursive(globalRecLock); -} - - -/* LockClaimGlobal -- claim the global non-recursive lock */ - -void (LockClaimGlobal)(void) -{ - int res; - - /* Ensure the global lock has been initialized */ - res = pthread_once(&isGlobalLockInit, globalLockInit); - AVER(res == 0); - LockClaim(globalLock); -} - - -/* LockReleaseGlobal -- release the global non-recursive lock */ - -void (LockReleaseGlobal)(void) -{ - LockRelease(globalLock); -} - - -/* 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/mps/code/lockut.c b/mps/code/lockut.c index a6e592988f1..9f9637914f0 100644 --- a/mps/code/lockut.c +++ b/mps/code/lockut.c @@ -57,7 +57,14 @@ static void inc(unsigned long i) #define COUNT 100000l static void *thread0(void *p) { + unsigned i; testlib_unused(p); + LockClaimGlobal(); + LockReleaseGlobal(); + for (i = 0; i < COUNT; ++i) + LockClaimGlobalRecursive(); + for (i = 0; i < COUNT; ++i) + LockReleaseGlobalRecursive(); inc(COUNT); return NULL; } diff --git a/mps/code/lockw3.c b/mps/code/lockw3.c index 53da970aed2..ba177c6b68b 100644 --- a/mps/code/lockw3.c +++ b/mps/code/lockw3.c @@ -1,7 +1,7 @@ /* lockw3.c: RECURSIVE LOCKS IN WIN32 * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * * .design: These are implemented using critical sections. * See the section titled "Synchronization functions" in the Groups @@ -23,14 +23,15 @@ #include "mpm.h" -#ifndef MPS_OS_W3 -#error "lockw3.c is specific to Win32 but MPS_OS_W3 not defined" +#if !defined(MPS_OS_W3) +#error "lockw3.c is specific to MPS_OS_W3" #endif #include "mpswin.h" SRCID(lockw3, "$Id$"); +#if defined(LOCK) /* .lock.win32: Win32 lock structure; uses CRITICAL_SECTION */ typedef struct LockStruct { @@ -75,7 +76,7 @@ void (LockClaim)(Lock lock) EnterCriticalSection(&lock->cs); /* This should be the first claim. Now we are inside the * critical section it is ok to check this. */ - AVER(lock->claims == 0); + AVER(lock->claims == 0); /* */ lock->claims = 1; } @@ -103,6 +104,15 @@ void (LockReleaseRecursive)(Lock lock) LeaveCriticalSection(&lock->cs); } +Bool (LockIsHeld)(Lock lock) +{ + if (TryEnterCriticalSection(&lock->cs)) { + Bool claimed = lock->claims > 0; + LeaveCriticalSection(&lock->cs); + return claimed; + } + return TRUE; +} /* Global locking is performed by normal locks. @@ -117,16 +127,41 @@ static Lock globalLock = &globalLockStruct; static Lock globalRecLock = &globalRecLockStruct; static Bool globalLockInit = FALSE; /* TRUE iff initialized */ +void LockInitGlobal(void) +{ + globalLock->claims = 0; + LockInit(globalLock); + globalRecLock->claims = 0; + LockInit(globalRecLock); + globalLockInit = TRUE; +} + +/* lockEnsureGlobalLock -- one-time initialization of global locks + * + * InitOnceExecuteOnce ensures that only one thread can be running the + * callback at a time, which allows to safely check globalLockInit. See + * + * but note that at time of writing (2018-06-27) the documentation has + * the arguments the wrong way round (parameter comes before context). + */ + +static BOOL CALLBACK lockEnsureGlobalLockCallback(INIT_ONCE *init_once, void *parameter, void **context) +{ + UNUSED(init_once); + AVER(parameter == UNUSED_POINTER); + UNUSED(context); + if (!globalLockInit) { + LockInitGlobal(); + } + return TRUE; +} static void lockEnsureGlobalLock(void) { - /* Ensure both global locks have been initialized. */ - /* There is a race condition initializing them. */ - if (!globalLockInit) { - LockInit(globalLock); - LockInit(globalRecLock); - globalLockInit = TRUE; - } + static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT; + BOOL b = InitOnceExecuteOnce(&init_once, lockEnsureGlobalLockCallback, + UNUSED_POINTER, NULL); + AVER(b); } void (LockClaimGlobalRecursive)(void) @@ -155,10 +190,21 @@ void (LockReleaseGlobal)(void) LockRelease(globalLock); } +void LockSetup(void) +{ + /* Nothing to do as MPS does not support fork() on Windows. */ +} + +#elif defined(LOCK_NONE) +#include "lockan.c" +#else +#error "No lock configuration." +#endif + /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/locus.c b/mps/code/locus.c index cc367b15a6c..b683aa0f3ec 100644 --- a/mps/code/locus.c +++ b/mps/code/locus.c @@ -1,7 +1,7 @@ /* locus.c: LOCUS MANAGER * * $Id$ - * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * * DESIGN * @@ -107,14 +107,68 @@ Bool GenDescCheck(GenDesc gen) { CHECKS(GenDesc, gen); /* nothing to check for zones */ - /* nothing to check for capacity */ + CHECKL(gen->capacity > 0); CHECKL(gen->mortality >= 0.0); CHECKL(gen->mortality <= 1.0); CHECKD_NOSIG(Ring, &gen->locusRing); + CHECKD_NOSIG(Ring, &gen->segRing); return TRUE; } +/* GenParamCheck -- check consistency of generation parameters */ + +ATTRIBUTE_UNUSED +static Bool GenParamCheck(GenParamStruct *params) +{ + CHECKL(params != NULL); + CHECKL(params->capacity > 0); + CHECKL(params->capacity <= SizeMAX / 1024); + CHECKL(params->mortality >= 0.0); + CHECKL(params->mortality <= 1.0); + return TRUE; +} + + +/* GenDescInit -- initialize a generation in a chain */ + +static void GenDescInit(GenDesc gen, GenParamStruct *params) +{ + TraceId ti; + + AVER(gen != NULL); + AVER(GenParamCheck(params)); + + gen->zones = ZoneSetEMPTY; + gen->capacity = params->capacity * 1024; + gen->mortality = params->mortality; + RingInit(&gen->locusRing); + RingInit(&gen->segRing); + gen->activeTraces = TraceSetEMPTY; + for (ti = 0; ti < TraceLIMIT; ++ti) + RingInit(&gen->trace[ti].traceRing); + gen->sig = GenDescSig; + AVERT(GenDesc, gen); +} + + +/* GenDescFinish -- finish a generation in a chain */ + +static void GenDescFinish(GenDesc gen) +{ + TraceId ti; + + AVERT(GenDesc, gen); + + gen->sig = SigInvalid; + RingFinish(&gen->locusRing); + RingFinish(&gen->segRing); + AVER(gen->activeTraces == TraceSetEMPTY); /* */ + for (ti = 0; ti < TraceLIMIT; ++ti) + RingFinish(&gen->trace[ti].traceRing); +} + + /* GenDescNewSize -- return effective size of generation */ Size GenDescNewSize(GenDesc gen) @@ -133,6 +187,86 @@ Size GenDescNewSize(GenDesc gen) } +/* genDescTraceStart -- notify generation of start of a trace */ + +void GenDescStartTrace(GenDesc gen, Trace trace) +{ + GenTrace genTrace; + + AVERT(GenDesc, gen); + AVERT(Trace, trace); + + AVER(!TraceSetIsMember(gen->activeTraces, trace)); + gen->activeTraces = TraceSetAdd(gen->activeTraces, trace); + genTrace = &gen->trace[trace->ti]; + AVER(RingIsSingle(&genTrace->traceRing)); + RingAppend(&trace->genRing, &genTrace->traceRing); + genTrace->condemned = 0; + genTrace->forwarded = 0; + genTrace->preservedInPlace = 0; +} + + +/* genDescEndTrace -- notify generation of end of a trace */ + +void GenDescEndTrace(GenDesc gen, Trace trace) +{ + GenTrace genTrace; + Size survived; + + AVERT(GenDesc, gen); + AVERT(Trace, trace); + + AVER(TraceSetIsMember(gen->activeTraces, trace)); + gen->activeTraces = TraceSetDel(gen->activeTraces, trace); + genTrace = &gen->trace[trace->ti]; + RingRemove(&genTrace->traceRing); + survived = genTrace->forwarded + genTrace->preservedInPlace; + AVER(survived <= genTrace->condemned); + + if (genTrace->condemned > 0) { + double mortality = 1.0 - survived / (double)genTrace->condemned; + double alpha = LocusMortalityALPHA; + gen->mortality = gen->mortality * (1 - alpha) + mortality * alpha; + EVENT6(TraceEndGen, trace, gen, genTrace->condemned, genTrace->forwarded, + genTrace->preservedInPlace, gen->mortality); + } +} + + +/* GenDescCondemned -- memory in a generation was condemned for a trace */ + +void GenDescCondemned(GenDesc gen, Trace trace, Size size) +{ + GenTrace genTrace; + + AVERT(GenDesc, gen); + AVERT(Trace, trace); + + genTrace = &gen->trace[trace->ti]; + genTrace->condemned += size; + trace->condemned += size; +} + + +/* GenDescSurvived -- memory in a generation survived a trace */ + +void GenDescSurvived(GenDesc gen, Trace trace, Size forwarded, + Size preservedInPlace) +{ + GenTrace genTrace; + + AVERT(GenDesc, gen); + AVERT(Trace, trace); + + genTrace = &gen->trace[trace->ti]; + genTrace->forwarded += forwarded; + genTrace->preservedInPlace += preservedInPlace; + trace->forwardedSize += forwarded; + trace->preservedInPlaceSize += preservedInPlace; +} + + /* GenDescTotalSize -- return total size of generation */ Size GenDescTotalSize(GenDesc gen) @@ -155,6 +289,7 @@ Size GenDescTotalSize(GenDesc gen) Res GenDescDescribe(GenDesc gen, mps_lib_FILE *stream, Count depth) { + Index i; Res res; Ring node, nextNode; @@ -166,12 +301,25 @@ Res GenDescDescribe(GenDesc gen, mps_lib_FILE *stream, Count depth) res = WriteF(stream, depth, "GenDesc $P {\n", (WriteFP)gen, " zones $B\n", (WriteFB)gen->zones, - " capacity $W\n", (WriteFW)gen->capacity, + " capacity $U\n", (WriteFW)gen->capacity, " mortality $D\n", (WriteFD)gen->mortality, + " activeTraces $B\n", (WriteFB)gen->activeTraces, NULL); if (res != ResOK) return res; + for (i = 0; i < NELEMS(gen->trace); ++i) { + GenTrace genTrace = &gen->trace[i]; + res = WriteF(stream, depth + 2, + "trace $U {\n", (WriteFW)i, + " condemned $U\n", (WriteFW)genTrace->condemned, + " forwarded $U\n", (WriteFW)genTrace->forwarded, + " preservedInPlace $U\n", (WriteFW)genTrace->preservedInPlace, + "}\n", 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); @@ -184,12 +332,35 @@ Res GenDescDescribe(GenDesc gen, mps_lib_FILE *stream, Count depth) } +/* ChainInit -- initialize a generation chain */ + +static void ChainInit(ChainStruct *chain, Arena arena, GenDescStruct *gens, + Count genCount) +{ + AVER(chain != NULL); + AVERT(Arena, arena); + AVER(gens != NULL); + AVER(genCount > 0); + + chain->arena = arena; + RingInit(&chain->chainRing); + chain->genCount = genCount; + chain->gens = gens; + chain->sig = ChainSig; + + AVERT(Chain, chain); + + RingAppend(&arena->chainRing, &chain->chainRing); +} + + /* ChainCreate -- create a generation chain */ Res ChainCreate(Chain *chainReturn, Arena arena, size_t genCount, GenParamStruct *params) { size_t i; + Size size; Chain chain; GenDescStruct *gens; Res res; @@ -199,46 +370,20 @@ Res ChainCreate(Chain *chainReturn, Arena arena, size_t genCount, AVERT(Arena, arena); AVER(genCount > 0); AVER(params != NULL); - for (i = 0; i < genCount; ++i) { - AVER(params[i].capacity > 0); - AVER(params[i].mortality > 0.0); - AVER(params[i].mortality < 1.0); - } - res = ControlAlloc(&p, arena, genCount * sizeof(GenDescStruct), FALSE); + size = sizeof(ChainStruct) + genCount * sizeof(GenDescStruct); + res = ControlAlloc(&p, arena, size); if (res != ResOK) return res; - gens = (GenDescStruct *)p; + chain = p; + gens = PointerAdd(p, sizeof(ChainStruct)); - for (i = 0; i < genCount; ++i) { - gens[i].zones = ZoneSetEMPTY; - gens[i].capacity = params[i].capacity; - gens[i].mortality = params[i].mortality; - RingInit(&gens[i].locusRing); - gens[i].sig = GenDescSig; - AVERT(GenDesc, &gens[i]); - } + for (i = 0; i < genCount; ++i) + GenDescInit(&gens[i], ¶ms[i]); + ChainInit(chain, arena, gens, genCount); - res = ControlAlloc(&p, arena, sizeof(ChainStruct), FALSE); - if (res != ResOK) - goto failChainAlloc; - chain = (Chain)p; - - chain->arena = arena; - RingInit(&chain->chainRing); - chain->activeTraces = TraceSetEMPTY; - chain->genCount = genCount; - chain->gens = gens; - chain->sig = ChainSig; - - RingAppend(&arena->chainRing, &chain->chainRing); - AVERT(Chain, chain); *chainReturn = chain; return ResOK; - -failChainAlloc: - ControlFree(arena, gens, genCount * sizeof(GenDescStruct)); - return res; } @@ -251,7 +396,6 @@ Bool ChainCheck(Chain chain) CHECKS(Chain, chain); CHECKU(Arena, chain->arena); CHECKD_NOSIG(Ring, &chain->chainRing); - CHECKL(TraceSetCheck(chain->activeTraces)); CHECKL(chain->genCount > 0); for (i = 0; i < chain->genCount; ++i) { CHECKD(GenDesc, &chain->gens[i]); @@ -265,23 +409,23 @@ Bool ChainCheck(Chain chain) void ChainDestroy(Chain chain) { Arena arena; + Size size; size_t genCount; size_t i; AVERT(Chain, chain); - AVER(chain->activeTraces == TraceSetEMPTY); arena = chain->arena; genCount = chain->genCount; RingRemove(&chain->chainRing); chain->sig = SigInvalid; - for (i = 0; i < genCount; ++i) { - RingFinish(&chain->gens[i].locusRing); - chain->gens[i].sig = SigInvalid; - } + for (i = 0; i < genCount; ++i) + GenDescFinish(&chain->gens[i]); + RingFinish(&chain->chainRing); - ControlFree(arena, chain->gens, genCount * sizeof(GenDescStruct)); - ControlFree(arena, chain, sizeof(ChainStruct)); + + size = sizeof(ChainStruct) + genCount * sizeof(GenDescStruct); + ControlFree(arena, chain, size); } @@ -308,61 +452,6 @@ GenDesc ChainGen(Chain chain, Index gen) } -/* 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; - - 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 gives more - * understandable telemetry than just reporting the added - * zones. */ - EVENT3(ArenaGenZoneAdd, arena, gen, 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) @@ -372,41 +461,20 @@ double ChainDeferral(Chain chain) AVERT(Chain, chain); - 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; - } + for (i = 0; i < chain->genCount; ++i) { + double genTime; + GenDesc gen = &chain->gens[i]; + if (gen->activeTraces != TraceSetEMPTY) + return DBL_MAX; + genTime = (double)gen->capacity - (double)GenDescNewSize(&chain->gens[i]); + if (genTime < time) + time = genTime; } return time; } -/* ChainStartGC -- called to notify start of GC for this chain */ - -void ChainStartGC(Chain chain, Trace trace) -{ - AVERT(Chain, chain); - AVERT(Trace, trace); - - chain->activeTraces = TraceSetAdd(chain->activeTraces, trace); -} - - -/* 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); -} - - /* ChainDescribe -- describe a chain */ Res ChainDescribe(Chain chain, mps_lib_FILE *stream, Count depth) @@ -422,7 +490,6 @@ Res ChainDescribe(Chain chain, mps_lib_FILE *stream, Count depth) 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; @@ -453,13 +520,14 @@ Res PoolGenInit(PoolGen pgen, GenDesc gen, Pool pool) pgen->pool = pool; pgen->gen = gen; RingInit(&pgen->genRing); - STATISTIC(pgen->segs = 0); + pgen->segs = 0; pgen->totalSize = 0; - STATISTIC(pgen->freeSize = 0); + pgen->freeSize = 0; + pgen->bufferedSize = 0; pgen->newSize = 0; - STATISTIC(pgen->oldSize = 0); + pgen->oldSize = 0; pgen->newDeferredSize = 0; - STATISTIC(pgen->oldDeferredSize = 0); + pgen->oldDeferredSize = 0; pgen->sig = PoolGenSig; AVERT(PoolGen, pgen); @@ -473,15 +541,14 @@ Res PoolGenInit(PoolGen pgen, GenDesc gen, Pool pool) void PoolGenFinish(PoolGen pgen) { AVERT(PoolGen, pgen); + AVER(pgen->segs == 0); AVER(pgen->totalSize == 0); + AVER(pgen->freeSize == 0); + AVER(pgen->bufferedSize == 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); - }); + AVER(pgen->oldSize == 0); + AVER(pgen->oldDeferredSize == 0); pgen->sig = SigInvalid; RingRemove(&pgen->genRing); @@ -497,88 +564,149 @@ Bool PoolGenCheck(PoolGen pgen) 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); - }); + CHECKL((pgen->totalSize == 0) == (pgen->segs == 0)); + CHECKL(pgen->totalSize >= pgen->segs * ArenaGrainSize(PoolArena(pgen->pool))); + CHECKL(pgen->totalSize == pgen->freeSize + pgen->bufferedSize + + pgen->newSize + pgen->oldSize + + pgen->newDeferredSize + pgen->oldDeferredSize); return TRUE; } -/* PoolGenAccountForFill -- accounting for allocation +/* PoolGenAccountForAlloc -- accounting for allocation of a segment */ + +static void PoolGenAccountForAlloc(PoolGen pgen, Size size) +{ + pgen->totalSize += size; + ++ pgen->segs; + pgen->freeSize += size; +} + + +/* PoolGenAlloc -- allocate a segment in a pool generation + * + * Allocate a segment belong to klass (which must be GCSegClass or a + * subclass), attach it to the generation, and update the accounting. + */ + +Res PoolGenAlloc(Seg *segReturn, PoolGen pgen, SegClass klass, Size size, + ArgList args) +{ + LocusPrefStruct pref; + Res res; + Seg seg; + ZoneSet zones, moreZones; + Arena arena; + GenDesc gen; + + AVER(segReturn != NULL); + AVERT(PoolGen, pgen); + AVERT(SegClass, klass); + AVER(IsSubclass(klass, GCSeg)); + AVER(size > 0); + 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, klass, &pref, size, pgen->pool, args); + if (res != ResOK) + return res; + + RingAppend(&gen->segRing, &SegGCSeg(seg)->genRing); + + moreZones = ZoneSetUnion(zones, ZoneSetOfSeg(arena, seg)); + gen->zones = moreZones; + + if (!ZoneSetSuper(zones, moreZones)) { + /* Tracking the whole zoneset for each generation gives more + * understandable telemetry than just reporting the added + * zones. */ + EVENT3(ArenaGenZoneAdd, arena, gen, moreZones); + } + + PoolGenAccountForAlloc(pgen, SegSize(seg)); + + *segReturn = seg; + return ResOK; +} + + +/* PoolGenAccountForFill -- accounting for allocation within a segment * * 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. + * BufferFill. * * See */ -void PoolGenAccountForFill(PoolGen pgen, Size size, Bool deferred) +void PoolGenAccountForFill(PoolGen pgen, Size size) { AVERT(PoolGen, pgen); - AVERT(Bool, deferred); - STATISTIC_STAT ({ - AVER(pgen->freeSize >= size); - pgen->freeSize -= size; - }); - if (deferred) - pgen->newDeferredSize += size; - else - pgen->newSize += size; + AVER(pgen->freeSize >= size); + pgen->freeSize -= size; + pgen->bufferedSize += size; } /* 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. + * Call this when the client program returns memory to the pool via + * BufferEmpty. The deferred flag indicates whether the accounting of + * the used memory (for the purpose of scheduling collections) should + * be deferred until later. * * See */ -void PoolGenAccountForEmpty(PoolGen pgen, Size unused, Bool deferred) +void PoolGenAccountForEmpty(PoolGen pgen, Size used, Size unused, Bool deferred) { AVERT(PoolGen, pgen); AVERT(Bool, deferred); + AVER(pgen->bufferedSize >= used + unused); + pgen->bufferedSize -= used + unused; if (deferred) { - AVER(pgen->newDeferredSize >= unused); - pgen->newDeferredSize -= unused; + pgen->newDeferredSize += used; } else { - AVER(pgen->newSize >= unused); - pgen->newSize -= unused; + pgen->newSize += used; } - STATISTIC(pgen->freeSize += unused); + pgen->freeSize += unused; } /* 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. + * Call this when memory is condemned via PoolWhiten, or when + * artificially ageing memory in PoolGenFree. The size parameter + * should be the amount of memory that is being condemned for the + * first time. The deferred flag is as for PoolGenAccountForEmpty. * * See */ -void PoolGenAccountForAge(PoolGen pgen, Size size, Bool deferred) +void PoolGenAccountForAge(PoolGen pgen, Size wasBuffered, Size wasNew, + Bool deferred) { AVERT(PoolGen, pgen); - + AVERT(Bool, deferred); + + AVER(pgen->bufferedSize >= wasBuffered); + pgen->bufferedSize -= wasBuffered; if (deferred) { - AVER(pgen->newDeferredSize >= size); - pgen->newDeferredSize -= size; - STATISTIC(pgen->oldDeferredSize += size); + AVER(pgen->newDeferredSize >= wasNew); + pgen->newDeferredSize -= wasNew; + pgen->oldDeferredSize += wasBuffered + wasNew; } else { - AVER(pgen->newSize >= size); - pgen->newSize -= size; - STATISTIC(pgen->oldSize += size); + AVER(pgen->newSize >= wasNew); + pgen->newSize -= wasNew; + pgen->oldSize += wasBuffered + wasNew; } } @@ -586,7 +714,7 @@ void PoolGenAccountForAge(PoolGen pgen, Size size, Bool deferred) /* PoolGenAccountForReclaim -- accounting for reclaiming * * Call this when reclaiming memory, passing the amount of memory that - * was reclaimed. The deferred flag is as for PoolGenAccountForFill. + * was reclaimed. The deferred flag is as for PoolGenAccountForEmpty. * * See */ @@ -596,16 +724,14 @@ void PoolGenAccountForReclaim(PoolGen pgen, Size reclaimed, Bool deferred) 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; - }); + if (deferred) { + AVER(pgen->oldDeferredSize >= reclaimed); + pgen->oldDeferredSize -= reclaimed; + } else { + AVER(pgen->oldSize >= reclaimed); + pgen->oldSize -= reclaimed; + } + pgen->freeSize += reclaimed; } @@ -621,11 +747,9 @@ void PoolGenAccountForReclaim(PoolGen pgen, Size reclaimed, Bool deferred) 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->oldDeferredSize >= oldSize); + pgen->oldDeferredSize -= oldSize; + pgen->oldSize += oldSize; AVER(pgen->newDeferredSize >= newSize); pgen->newDeferredSize -= newSize; pgen->newSize += newSize; @@ -637,10 +761,8 @@ void PoolGenUndefer(PoolGen pgen, Size oldSize, Size newSize) void PoolGenAccountForSegSplit(PoolGen pgen) { AVERT(PoolGen, pgen); - STATISTIC_STAT ({ - AVER(pgen->segs >= 1); /* must be at least one segment to split */ - ++ pgen->segs; - }); + AVER(pgen->segs >= 1); /* must be at least one segment to split */ + ++ pgen->segs; } @@ -649,10 +771,28 @@ void PoolGenAccountForSegSplit(PoolGen pgen) void PoolGenAccountForSegMerge(PoolGen pgen) { AVERT(PoolGen, pgen); - STATISTIC_STAT ({ - AVER(pgen->segs >= 2); /* must be at least two segments to merge */ - -- pgen->segs; - }); + AVER(pgen->segs >= 2); /* must be at least two segments to merge */ + -- pgen->segs; +} + + +/* PoolGenAccountForFree -- accounting for the freeing of a segment */ + +static void PoolGenAccountForFree(PoolGen pgen, Size size, + Size oldSize, Size newSize, + Bool deferred) +{ + /* Pretend to age and reclaim the contents of the segment to ensure + * that the entire segment is accounted as free. */ + PoolGenAccountForAge(pgen, 0, newSize, deferred); + PoolGenAccountForReclaim(pgen, oldSize + newSize, deferred); + + AVER(pgen->totalSize >= size); + pgen->totalSize -= size; + AVER(pgen->segs > 0); + -- pgen->segs; + AVER(pgen->freeSize >= size); + pgen->freeSize -= size; } @@ -660,7 +800,7 @@ void PoolGenAccountForSegMerge(PoolGen pgen) * * Pass the amount of memory in the segment that is accounted as free, * old, or new, respectively. The deferred flag is as for - * PoolGenAccountForFill. + * PoolGenAccountForEmpty. * * See */ @@ -676,19 +816,10 @@ void PoolGenFree(PoolGen pgen, Seg seg, Size freeSize, Size oldSize, 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); + PoolGenAccountForFree(pgen, size, oldSize, newSize, deferred); + + RingRemove(&SegGCSeg(seg)->genRing); - AVER(pgen->totalSize >= size); - pgen->totalSize -= size; - STATISTIC_STAT ({ - AVER(pgen->segs > 0); - -- pgen->segs; - AVER(pgen->freeSize >= size); - pgen->freeSize -= size; - }); SegFree(seg); } @@ -698,20 +829,24 @@ void PoolGenFree(PoolGen pgen, Seg seg, Size freeSize, Size oldSize, Res PoolGenDescribe(PoolGen pgen, mps_lib_FILE *stream, Count depth) { Res res; + PoolClass poolClass; if (!TESTT(PoolGen, pgen)) - return ResFAIL; + return ResPARAM; if (stream == NULL) - return ResFAIL; + return ResPARAM; + + poolClass = ClassOfPoly(Pool, pgen->pool); 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, + (WriteFS)ClassName(poolClass), " segs $U\n", (WriteFU)pgen->segs, " totalSize $U\n", (WriteFU)pgen->totalSize, " freeSize $U\n", (WriteFU)pgen->freeSize, + " bufferedSize $U\n", (WriteFU)pgen->bufferedSize, " oldSize $U\n", (WriteFU)pgen->oldSize, " oldDeferredSize $U\n", (WriteFU)pgen->oldDeferredSize, " newSize $U\n", (WriteFU)pgen->newSize, @@ -726,18 +861,14 @@ Res PoolGenDescribe(PoolGen pgen, mps_lib_FILE *stream, Count depth) void LocusInit(Arena arena) { - GenDesc gen = &arena->topGen; + GenParamStruct params; - /* Can't check arena, because it's not been inited. */ - - /* TODO: The mortality estimate here is unjustifiable. Dynamic generation - decision making needs to be improved and this constant removed. */ - gen->zones = ZoneSetEMPTY; - gen->capacity = 0; /* unused */ - gen->mortality = 0.51; - RingInit(&gen->locusRing); - gen->sig = GenDescSig; - AVERT(GenDesc, gen); + AVER(arena != NULL); /* not initialized yet. */ + + params.capacity = 1; /* unused since top generation is not on any chain */ + params.mortality = 0.5; + + GenDescInit(&arena->topGen, ¶ms); } @@ -745,12 +876,9 @@ void LocusInit(Arena arena) void LocusFinish(Arena arena) { - GenDesc gen = &arena->topGen; - /* Can't check arena, because it's being finished. */ - - gen->sig = SigInvalid; - RingFinish(&gen->locusRing); + AVER(arena != NULL); + GenDescFinish(&arena->topGen); } @@ -766,7 +894,7 @@ Bool LocusCheck(Arena arena) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2016 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/locus.h b/mps/code/locus.h index d1d9716303e..7c71a714dd1 100644 --- a/mps/code/locus.h +++ b/mps/code/locus.h @@ -17,11 +17,23 @@ typedef struct GenParamStruct *GenParam; typedef struct GenParamStruct { - Size capacity; /* capacity in kB */ - double mortality; + Size capacity; /* capacity in kB */ + double mortality; /* predicted mortality */ } GenParamStruct; +/* GenTrace -- per-generation per-trace structure */ + +typedef struct GenTraceStruct *GenTrace; + +typedef struct GenTraceStruct { + RingStruct traceRing; /* link in ring of generations condemned by trace */ + Size condemned; /* size of objects condemned by the trace */ + Size forwarded; /* size of objects that were forwarded by the trace */ + Size preservedInPlace; /* size of objects preserved in place by the trace */ +} GenTraceStruct; + + /* GenDesc -- descriptor of a generation in a chain */ typedef struct GenDescStruct *GenDesc; @@ -30,17 +42,18 @@ typedef struct GenDescStruct *GenDesc; typedef struct GenDescStruct { Sig sig; - ZoneSet zones; /* zoneset for this generation */ - Size capacity; /* capacity in kB */ - double mortality; + ZoneSet zones; /* zoneset for this generation */ + Size capacity; /* capacity in bytes */ + double mortality; /* predicted mortality */ RingStruct locusRing; /* Ring of all PoolGen's in this GenDesc (locus) */ + RingStruct segRing; /* Ring of GCSegs in this generation */ + TraceSet activeTraces; /* set of traces collecting this generation */ + GenTraceStruct trace[TraceLIMIT]; } GenDescStruct; /* PoolGen -- descriptor of a generation in a pool */ -typedef struct PoolGenStruct *PoolGen; - #define PoolGenSig ((Sig)0x519B009E) /* SIGnature POOl GEn */ typedef struct PoolGenStruct { @@ -51,13 +64,14 @@ typedef struct PoolGenStruct { RingStruct genRing; /* 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) */ + Size segs; /* number of segments */ + Size totalSize; /* total (sum of segment sizes) */ + Size freeSize; /* unused (free or lost to fragmentation) */ + Size bufferedSize; /* held in buffers but not condemned yet */ + Size newSize; /* allocated since last collection */ + Size oldSize; /* allocated prior to last collection */ + Size newDeferredSize; /* new (but deferred) */ + Size oldDeferredSize; /* old (but deferred) */ } PoolGenStruct; @@ -69,7 +83,6 @@ typedef struct mps_chain_s { Sig sig; Arena arena; RingStruct chainRing; /* list of chains in the arena */ - TraceSet activeTraces; /* set of traces collecting this chain */ size_t genCount; /* number of generations */ GenDesc gens; /* the array of generations */ } ChainStruct; @@ -78,7 +91,12 @@ typedef struct mps_chain_s { extern Bool GenDescCheck(GenDesc gen); extern Size GenDescNewSize(GenDesc gen); extern Size GenDescTotalSize(GenDesc gen); +extern void GenDescStartTrace(GenDesc gen, Trace trace); +extern void GenDescEndTrace(GenDesc gen, Trace trace); +extern void GenDescCondemned(GenDesc gen, Trace trace, Size size); +extern void GenDescSurvived(GenDesc gen, Trace trace, Size forwarded, Size preservedInPlace); extern Res GenDescDescribe(GenDesc gen, mps_lib_FILE *stream, Count depth); +#define GenDescOfTraceRing(node, trace) PARENT(GenDescStruct, trace[trace->ti], RING_ELT(GenTrace, traceRing, node)) extern Res ChainCreate(Chain *chainReturn, Arena arena, size_t genCount, GenParam params); @@ -86,8 +104,6 @@ extern void ChainDestroy(Chain chain); extern Bool ChainCheck(Chain chain); extern double ChainDeferral(Chain chain); -extern void ChainStartGC(Chain chain, Trace trace); -extern void ChainEndGC(Chain chain, Trace trace); extern size_t ChainGens(Chain chain); extern GenDesc ChainGen(Chain chain, Index gen); extern Res ChainDescribe(Chain chain, mps_lib_FILE *stream, Count depth); @@ -95,13 +111,13 @@ 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 Res PoolGenAlloc(Seg *segReturn, PoolGen pgen, SegClass klass, + Size size, 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 PoolGenAccountForFill(PoolGen pgen, Size size); +extern void PoolGenAccountForEmpty(PoolGen pgen, Size used, Size unused, Bool deferred); +extern void PoolGenAccountForAge(PoolGen pgen, Size wasBuffered, Size wasNew, 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); diff --git a/mps/code/locusss.c b/mps/code/locusss.c index b3769dd674a..8af79958c56 100644 --- a/mps/code/locusss.c +++ b/mps/code/locusss.c @@ -5,7 +5,6 @@ */ #include "mpscmvff.h" -#include "mpscmv.h" #include "mpslib.h" #include "mpsavm.h" #include "testlib.h" @@ -169,8 +168,8 @@ static void testInArena(mps_arena_t arena, FALSE, FALSE, TRUE), "Create LO MFFV"); - die(mps_pool_create(&temppool, arena, mps_class_mv(), - chunkSize, chunkSize, chunkSize), + die(mps_pool_create_k(&temppool, arena, mps_class_mvff(), + mps_args_none), "Create TEMP"); if(failcase) { diff --git a/mps/code/locv.c b/mps/code/locv.c index 06d2722dac3..4bcfd936b39 100644 --- a/mps/code/locv.c +++ b/mps/code/locv.c @@ -1,7 +1,7 @@ /* locv.c: LEAF OBJECT POOL CLASS COVERAGE TEST * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. * * This is (not much of) a coverage test for the Leaf Object * pool (PoolClassLO). @@ -165,14 +165,13 @@ static void stepper(mps_addr_t addr, mps_fmt_t fmt, mps_pool_t pool, pcount = p; *pcount += 1; - return; } /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2014 Ravenbrook Limited . + * Copyright (c) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/message.c b/mps/code/message.c index eba94182837..c22fe2e5b28 100644 --- a/mps/code/message.c +++ b/mps/code/message.c @@ -1,7 +1,7 @@ /* message.c: MPS/CLIENT MESSAGES * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. * * DESIGN * @@ -12,6 +12,8 @@ * .purpose: Provide the generic part of the MPS / Client message * interface. Messages are instances of Message Classes; much of the * "real work" goes on in the modules that provide the actual messages. + * + * TODO: Consider using protocol classes for messages. */ #include "bt.h" @@ -45,14 +47,14 @@ Bool MessageTypeCheck(MessageType type) /* See .message.clocked. Currently finalization messages are the */ /* only ones that can be numerous. */ -#define MessageIsClocked(message) ((message)->class->type \ - != MessageTypeFINALIZATION) +#define MessageIsClocked(message) \ + ((message)->klass->type != MessageTypeFINALIZATION) Bool MessageCheck(Message message) { CHECKS(Message, message); CHECKU(Arena, message->arena); - CHECKD(MessageClass, message->class); + CHECKD(MessageClass, message->klass); CHECKD_NOSIG(Ring, &message->queueRing); /* postedClock is uncheckable for clocked message types, */ /* but must be 0 for unclocked message types: */ @@ -61,32 +63,32 @@ Bool MessageCheck(Message message) return TRUE; } -Bool MessageClassCheck(MessageClass class) +Bool MessageClassCheck(MessageClass klass) { - CHECKS(MessageClass, class); - CHECKL(class->name != NULL); - CHECKL(MessageTypeCheck(class->type)); - CHECKL(FUNCHECK(class->delete)); - CHECKL(FUNCHECK(class->finalizationRef)); - CHECKL(FUNCHECK(class->gcLiveSize)); - CHECKL(FUNCHECK(class->gcCondemnedSize)); - CHECKL(FUNCHECK(class->gcNotCondemnedSize)); - CHECKL(FUNCHECK(class->gcStartWhy)); - CHECKL(class->endSig == MessageClassSig); + CHECKS(MessageClass, klass); + CHECKL(klass->name != NULL); + CHECKL(MessageTypeCheck(klass->type)); + CHECKL(FUNCHECK(klass->delete)); + CHECKL(FUNCHECK(klass->finalizationRef)); + CHECKL(FUNCHECK(klass->gcLiveSize)); + CHECKL(FUNCHECK(klass->gcCondemnedSize)); + CHECKL(FUNCHECK(klass->gcNotCondemnedSize)); + CHECKL(FUNCHECK(klass->gcStartWhy)); + CHECKL(klass->endSig == MessageClassSig); return TRUE; } -void MessageInit(Arena arena, Message message, MessageClass class, +void MessageInit(Arena arena, Message message, MessageClass klass, MessageType type) { AVERT(Arena, arena); AVER(message != NULL); - AVERT(MessageClass, class); + AVERT(MessageClass, klass); AVERT(MessageType, type); message->arena = arena; - message->class = class; + message->klass = klass; RingInit(&message->queueRing); message->postedClock = 0; message->sig = MessageSig; @@ -279,20 +281,20 @@ void MessageDiscard(Arena arena, Message message) /* Message Methods, Generic * - * (Some of these dispatch on message->class). + * (Some of these dispatch on message->klass). */ /* Return the type of a message */ MessageType MessageGetType(Message message) { - MessageClass class; + MessageClass klass; AVERT(Message, message); - class = message->class; - AVERT(MessageClass, class); + klass = message->klass; + AVERT(MessageClass, klass); - return class->type; + return klass->type; } /* Return the class of a message */ @@ -300,7 +302,7 @@ MessageClass MessageGetClass(Message message) { AVERT(Message, message); - return message->class; + return message->klass; } Clock MessageGetClock(Message message) @@ -314,7 +316,7 @@ static void MessageDelete(Message message) { AVERT(Message, message); - (*message->class->delete)(message); + (*message->klass->delete)(message); } @@ -331,9 +333,7 @@ void MessageFinalizationRef(Ref *refReturn, Arena arena, AVERT(Message, message); AVER(MessageGetType(message) == MessageTypeFINALIZATION); - (*message->class->finalizationRef)(refReturn, arena, message); - - return; + (*message->klass->finalizationRef)(refReturn, arena, message); } Size MessageGCLiveSize(Message message) @@ -341,7 +341,7 @@ Size MessageGCLiveSize(Message message) AVERT(Message, message); AVER(MessageGetType(message) == MessageTypeGC); - return (*message->class->gcLiveSize)(message); + return (*message->klass->gcLiveSize)(message); } Size MessageGCCondemnedSize(Message message) @@ -349,7 +349,7 @@ Size MessageGCCondemnedSize(Message message) AVERT(Message, message); AVER(MessageGetType(message) == MessageTypeGC); - return (*message->class->gcCondemnedSize)(message); + return (*message->klass->gcCondemnedSize)(message); } Size MessageGCNotCondemnedSize(Message message) @@ -357,7 +357,7 @@ Size MessageGCNotCondemnedSize(Message message) AVERT(Message, message); AVER(MessageGetType(message) == MessageTypeGC); - return (*message->class->gcNotCondemnedSize)(message); + return (*message->klass->gcNotCondemnedSize)(message); } const char *MessageGCStartWhy(Message message) @@ -365,7 +365,7 @@ const char *MessageGCStartWhy(Message message) AVERT(Message, message); AVER(MessageGetType(message) == MessageTypeGCSTART); - return (*message->class->gcStartWhy)(message); + return (*message->klass->gcStartWhy)(message); } @@ -427,7 +427,7 @@ const char *MessageNoGCStartWhy(Message message) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/messtest.c b/mps/code/messtest.c index 96deeebfc58..0958d9319c7 100644 --- a/mps/code/messtest.c +++ b/mps/code/messtest.c @@ -1,7 +1,7 @@ /* messtest.c: MESSAGE TEST * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. */ #include "mpm.h" @@ -71,18 +71,17 @@ static void topMessageType(MessageType *typeReturn, Arena arena) /* postDummyMessage -- post a dummy message */ -static void postDummyMessage(Arena arena, MessageClass class, +static void postDummyMessage(Arena arena, MessageClass klass, MessageType type) { void *p; Message message; - die((mps_res_t)ControlAlloc(&p, arena, sizeof(MessageStruct), FALSE), + die((mps_res_t)ControlAlloc(&p, arena, sizeof(MessageStruct)), "AllocMessage"); message = (Message)p; - MessageInit(arena, message, class, type); + MessageInit(arena, message, klass, type); MessagePost(arena, message); - return; } @@ -255,7 +254,7 @@ static void testGetEmpty(Arena arena) #define testArenaSIZE (((size_t)64)<<20) -extern int main(int argc, char *argv[]) +int main(int argc, char *argv[]) { mps_arena_t mpsArena; Arena arena; @@ -277,7 +276,7 @@ extern int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2014 Ravenbrook Limited . + * Copyright (c) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/misc.h b/mps/code/misc.h index 9b22522558a..3a5d3c18e60 100644 --- a/mps/code/misc.h +++ b/mps/code/misc.h @@ -109,19 +109,26 @@ typedef const struct SrcIdStruct { #define NELEMS(a) (sizeof(a)/sizeof((a)[0])) -/* DISCARD -- discards an expression, but checks syntax +/* DISCARD_EXP -- discard an expression, but check syntax + * + * .discard: DISCARD_EXP uses sizeof so that the expression is not + * evaluated and yet the compiler will check that it is a valid + * expression. The conditional is compared with zero so it can + * designate a bitfield object. + */ + +#define DISCARD_EXP(expr) ((void)sizeof((expr)!=0)) + + +/* DISCARD -- discards an expression in statement context, but checks syntax * * The argument is an expression; the expansion followed by a semicolon * is syntactically a statement (to avoid it being used in computation). - * - * .discard: DISCARD uses sizeof so that the expression is not evaluated - * and yet the compiler will check that it is a valid expression. The - * conditional is compared with zero so it can designate a bitfield object. */ #define DISCARD(expr) \ BEGIN \ - (void)sizeof((expr)!=0); \ + DISCARD_EXP(expr); \ END diff --git a/mps/code/mpm.c b/mps/code/mpm.c index aba1ac2f5cb..71072b67c9d 100644 --- a/mps/code/mpm.c +++ b/mps/code/mpm.c @@ -1,7 +1,7 @@ /* mpm.c: GENERAL MPM SUPPORT * * $Id$ - * Copyright (c) 2001-2015 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. * * .purpose: Miscellaneous support for the implementation of the MPM * and pool classes. @@ -9,6 +9,7 @@ * .sources: */ #include "check.h" +#include "misc.h" #include "mpm.h" #include "vm.h" @@ -88,6 +89,11 @@ Bool MPMCheck(void) * . */ CHECKL(StackProbeDEPTH * sizeof(Word) < PageSize()); + /* Check these values will fit in their bitfield. */ + CHECKL(WB_DEFER_INIT <= ((1ul << WB_DEFER_BITS) - 1)); + CHECKL(WB_DEFER_DELAY <= ((1ul << WB_DEFER_BITS) - 1)); + CHECKL(WB_DEFER_HIT <= ((1ul << WB_DEFER_BITS) - 1)); + return TRUE; } @@ -614,16 +620,19 @@ Res WriteF_firstformat_v(mps_lib_FILE *stream, Count depth, size_t StringLength(const char *s) { - size_t i; + size_t i = 0; AVER(s != NULL); - for(i = 0; s[i] != '\0'; i++) - NOOP; - return(i); + while (s[i] != '\0') + ++i; + + return i; } +#if 0 /* This code is currently not in use in the MPS */ + /* StringEqual -- slow substitute for (strcmp == 0) */ Bool StringEqual(const char *s1, const char *s2) @@ -644,11 +653,153 @@ Bool StringEqual(const char *s1, const char *s2) return TRUE; } +#endif /* not currently in use */ + + +/* Random -- a random number generator + * + * TODO: This is a copy of the generator from testlib.c, which has + * extensive notes and verification tests. The notes need to go to a + * design document, and the tests to a test. + */ + +static unsigned RandomSeed = 1; +#define Random_m 2147483647UL +#define Random_a 48271UL +unsigned Random32(void) +{ + /* requires m == 2^31-1, a < 2^16 */ + unsigned bot = Random_a * (RandomSeed & 0x7FFF); + unsigned top = Random_a * (RandomSeed >> 15); + AVER(UINT_MAX >= 4294967295U); + RandomSeed = bot + ((top & 0xFFFF) << 15) + (top >> 16); + if (RandomSeed > Random_m) + RandomSeed -= Random_m; + return RandomSeed; +} + +Word RandomWord(void) +{ + Word word = 0; + Index i; + for (i = 0; i < MPS_WORD_WIDTH; i += 31) + word = (word << 31) | Random32(); + return word; +} + + +/* QuickSort -- non-recursive bounded sort + * + * We can't rely on the standard library's qsort, which might have + * O(n) stack usage. This version does not recurse. + */ + +#ifdef QUICKSORT_DEBUG +static Bool quickSorted(void *array[], Count length, + QuickSortCompare compare, void *closure) +{ + Index i; + if (length > 0) { + for (i = 0; i < length - 1; ++i) { + if (compare(array[i], array[i+1], closure) == CompareGREATER) + return FALSE; + } + } + return TRUE; +} +#endif + +void QuickSort(void *array[], Count length, + QuickSortCompare compare, void *closure, + SortStruct *sortStruct) +{ + Index left, right, sp, lo, hi, leftLimit, rightBase; + void *pivot, *temp; + + AVER(array != NULL); + /* can't check length */ + AVER(FUNCHECK(compare)); + /* can't check closure */ + AVER(sortStruct != NULL); + + sp = 0; + left = 0; + right = length; + + for (;;) { + while (right - left > 1) { /* only need to sort if two or more */ + /* Pick a random pivot. */ + pivot = array[left + RandomWord() % (right - left)]; + + /* Hoare partition: scan from left to right, dividing it into + elements less than the pivot and elements greater or + equal. */ + lo = left; + hi = right; + for (;;) { + while (compare(array[lo], pivot, closure) == CompareLESS) + ++lo; + do + --hi; + while (compare(pivot, array[hi], closure) == CompareLESS); + if (lo >= hi) + break; + temp = array[hi]; + array[hi] = array[lo]; + array[lo] = temp; + ++lo; /* step over what we just swapped */ + } + + /* After partition, if we ended up at a pivot, then it is in its + final position and we must skip it to ensure termination. + This handles the case where the pivot is at the start of the + array, and one of the partitions is the whole array, for + example. */ + if (lo == hi) { + AVER_CRITICAL(array[hi] == pivot); /* and it's in place */ + leftLimit = lo; + rightBase = lo + 1; + } else { + AVER_CRITICAL(lo == hi + 1); + leftLimit = lo; + rightBase = lo; + } + + /* Sort the smaller part now, so that we're sure to use at most + log2 length stack levels. Push the larger part on the stack + for later. */ + AVER_CRITICAL(sp < sizeof sortStruct->stack / sizeof sortStruct->stack[0]); + if (leftLimit - left < right - rightBase) { + sortStruct->stack[sp].left = rightBase; + sortStruct->stack[sp].right = right; + ++sp; + right = leftLimit; + } else { + sortStruct->stack[sp].left = left; + sortStruct->stack[sp].right = leftLimit; + ++sp; + left = rightBase; + } + } + + if (sp == 0) + break; + + --sp; + left = sortStruct->stack[sp].left; + right = sortStruct->stack[sp].right; + AVER_CRITICAL(left < right); /* we will have done a zero-length part first */ + } + +#ifdef QUICKSORT_DEBUG + AVER(quickSorted(array, length, compare, closure)); +#endif +} /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2015 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/mpm.h b/mps/code/mpm.h index 4390c37153d..855a18172f2 100644 --- a/mps/code/mpm.h +++ b/mps/code/mpm.h @@ -1,12 +1,18 @@ /* mpm.h: MEMORY POOL MANAGER DEFINITIONS * * $Id$ - * Copyright (c) 2001-2015 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 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 * an Init method, there's a name clash. We resolve this by calling the * accessor BufferGetInit. + * + * .critical.macros: In manual-allocation-bound programs using MVFF, + * PoolFree and the Land generic functions are on the critical path + * via mps_free. In non-checking varieties we provide macro + * alternatives to these functions that call the underlying methods + * directly, giving a few percent improvement in performance. */ #ifndef mpm_h @@ -18,6 +24,7 @@ #include "event.h" #include "lock.h" +#include "prmc.h" #include "prot.h" #include "sp.h" #include "th.h" @@ -171,6 +178,15 @@ extern Res WriteF_firstformat_v(mps_lib_FILE *stream, Count depth, extern size_t StringLength(const char *s); extern Bool StringEqual(const char *s1, const char *s2); +extern unsigned Random32(void); +extern Word RandomWord(void); + +typedef Compare QuickSortCompare(void *left, void *right, + void *closure); +extern void QuickSort(void *array[], Count length, + QuickSortCompare compare, void *closure, + SortStruct *sortStruct); + /* Version Determination * @@ -181,9 +197,9 @@ extern char *MPSVersion(void); /* Pool Interface -- see impl.c.pool */ -extern Res PoolInit(Pool pool, Arena arena, PoolClass class, ArgList args); +extern Res PoolInit(Pool pool, Arena arena, PoolClass klass, ArgList args); extern void PoolFinish(Pool pool); -extern Bool PoolClassCheck(PoolClass class); +extern Bool PoolClassCheck(PoolClass klass); extern Bool PoolCheck(Pool pool); extern Res PoolDescribe(Pool pool, mps_lib_FILE *stream, Count depth); @@ -193,7 +209,12 @@ extern Res PoolDescribe(Pool pool, mps_lib_FILE *stream, Count depth); #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) +#define PoolHasAttr(pool, Attr) ((ClassOfPoly(Pool, pool)->attr & (Attr)) != 0) +#define PoolSizeGrains(pool, size) ((size) >> (pool)->alignShift) +#define PoolGrainsSize(pool, grains) ((grains) << (pool)->alignShift) +#define PoolIndexOfAddr(base, pool, p) \ + (AddrOffset((base), (p)) >> (pool)->alignShift) +#define PoolAddrOfIndex(base, pool, i) AddrAdd(base, PoolGrainsSize(pool, i)) extern Bool PoolFormat(Format *formatReturn, Pool pool); @@ -204,70 +225,36 @@ extern Bool PoolOfRange(Pool *poolReturn, Arena arena, Addr base, Addr limit); extern Bool PoolHasAddr(Pool pool, Addr addr); extern Bool PoolHasRange(Pool pool, Addr base, Addr limit); -extern Res PoolCreate(Pool *poolReturn, Arena arena, PoolClass class, +extern Res PoolCreate(Pool *poolReturn, Arena arena, PoolClass klass, ArgList args); extern void PoolDestroy(Pool pool); extern BufferClass PoolDefaultBufferClass(Pool pool); -extern Res PoolAlloc(Addr *pReturn, Pool pool, Size size, - Bool withReservoirPermit); -extern void PoolFree(Pool pool, Addr old, Size size); +extern Res PoolAlloc(Addr *pReturn, Pool pool, Size size); +extern void (PoolFree)(Pool pool, Addr old, Size size); +extern PoolGen PoolSegPoolGen(Pool pool, Seg seg); extern Res PoolTraceBegin(Pool pool, Trace trace); -extern Res PoolAccess(Pool pool, Seg seg, Addr addr, - AccessSet mode, MutatorFaultContext context); -extern Res PoolWhiten(Pool pool, Trace trace, Seg seg); -extern void PoolGrey(Pool pool, Trace trace, Seg seg); -extern void PoolBlacken(Pool pool, TraceSet traceSet, Seg seg); -extern Res PoolScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg); -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 void PoolTraceEnd(Pool pool, Trace trace); -extern Res PoolAddrObject(Addr *pReturn, Pool pool, Seg seg, Addr addr); -extern void PoolWalk(Pool pool, Seg seg, FormattedObjectsVisitor f, - void *v, size_t s); 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, - Bool withReservoirPermit); -extern Res PoolTrivAlloc(Addr *pReturn, Pool pool, Size size, - Bool withReservoirPermit); +extern Res PoolAbsInit(Pool pool, Arena arena, PoolClass klass, ArgList arg); +extern void PoolAbsFinish(Inst inst); +extern Res PoolNoAlloc(Addr *pReturn, Pool pool, Size size); +extern Res PoolTrivAlloc(Addr *pReturn, Pool pool, Size size); extern void PoolNoFree(Pool pool, Addr old, Size size); extern void PoolTrivFree(Pool pool, Addr old, Size size); +extern PoolGen PoolNoSegPoolGen(Pool pool, Seg seg); extern Res PoolNoBufferFill(Addr *baseReturn, Addr *limitReturn, - Pool pool, Buffer buffer, Size size, - Bool withReservoirPermit); + Pool pool, Buffer buffer, Size size); extern Res PoolTrivBufferFill(Addr *baseReturn, Addr *limitReturn, - Pool pool, Buffer buffer, Size size, - Bool withReservoirPermit); -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, Count depth); + Pool pool, Buffer buffer, Size size); +extern void PoolNoBufferEmpty(Pool pool, Buffer buffer); +extern void PoolSegBufferEmpty(Pool pool, Buffer buffer); +extern void PoolTrivBufferEmpty(Pool pool, Buffer buffer); +extern Res PoolAbsDescribe(Inst inst, 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, - AccessSet mode, MutatorFaultContext context); -extern Res PoolSegAccess(Pool pool, Seg seg, Addr addr, - AccessSet mode, MutatorFaultContext context); -extern Res PoolSingleAccess(Pool pool, Seg seg, Addr addr, - AccessSet mode, MutatorFaultContext context); -extern Res PoolNoWhiten(Pool pool, Trace trace, Seg seg); -extern Res PoolTrivWhiten(Pool pool, Trace trace, Seg seg); -extern void PoolNoGrey(Pool pool, Trace trace, Seg seg); -extern void PoolTrivGrey(Pool pool, Trace trace, Seg seg); -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 void PoolTrivTraceEnd(Pool pool, Trace trace); extern void PoolNoRampBegin(Pool pool, Buffer buf, Bool collectAll); extern void PoolTrivRampBegin(Pool pool, Buffer buf, Bool collectAll); extern void PoolNoRampEnd(Pool pool, Buffer buf); @@ -276,50 +263,36 @@ extern Res PoolNoFramePush(AllocFrame *frameReturn, Pool pool, Buffer buf); 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, FormattedObjectsVisitor f, - void *p, size_t s); 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) \ - ((PoolClass)ProtocolClassSuperclassPoly((pool)->class)) - +/* See .critical.macros. */ +#define PoolFreeMacro(pool, old, size) Method(Pool, pool, free)(pool, old, size) +#if !defined(AVER_AND_CHECK_ALL) +#define PoolFree(pool, old, size) PoolFreeMacro(pool, old, size) +#endif /* !defined(AVER_AND_CHECK_ALL) */ /* Abstract Pool Classes Interface -- see */ -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 AbstractBufferPoolClass AbstractBufferPoolClassGet(void); -extern AbstractBufferPoolClass AbstractSegBufPoolClassGet(void); -extern AbstractScanPoolClass AbstractScanPoolClassGet(void); -extern AbstractCollectPoolClass AbstractCollectPoolClassGet(void); - -/* DEFINE_POOL_CLASS - * - * Convenience macro -- see . */ - -#define DEFINE_POOL_CLASS(className, var) \ - DEFINE_ALIAS_CLASS(className, PoolClass, var) - -#define POOL_SUPERCLASS(className) \ - ((PoolClass)SUPERCLASS(className)) +extern void PoolClassMixInBuffer(PoolClass klass); +extern void PoolClassMixInCollect(PoolClass klass); +DECLARE_CLASS(Inst, PoolClass, InstClass); +DECLARE_CLASS(Pool, AbstractPool, Inst); +DECLARE_CLASS(Pool, AbstractBufferPool, AbstractPool); +DECLARE_CLASS(Pool, AbstractSegBufPool, AbstractBufferPool); +typedef Pool AbstractCollectPool; +#define AbstractCollectPoolCheck PoolCheck +DECLARE_CLASS(Pool, AbstractCollectPool, AbstractSegBufPool); /* Message Interface -- see */ /* -- Internal (MPM) Interface -- functions for message originator */ extern Bool MessageCheck(Message message); -extern Bool MessageClassCheck(MessageClass class); +extern Bool MessageClassCheck(MessageClass klass); extern Bool MessageTypeCheck(MessageType type); extern void MessageInit(Arena arena, Message message, - MessageClass class, MessageType type); + MessageClass klass, MessageType type); extern void MessageFinish(Message message); extern Arena MessageArena(Message message); extern Bool MessageOnQueue(Message message); @@ -394,12 +367,16 @@ extern Bool TraceIdCheck(TraceId id); extern Bool TraceSetCheck(TraceSet ts); extern Bool TraceCheck(Trace trace); extern Res TraceCreate(Trace *traceReturn, Arena arena, int why); -extern void TraceDestroy(Trace trace); +extern void TraceDestroyInit(Trace trace); +extern void TraceDestroyFinished(Trace trace); +extern Bool TraceIsEmpty(Trace trace); extern Res TraceAddWhite(Trace trace, Seg seg); -extern Res TraceCondemnZones(Trace trace, ZoneSet condemnedSet); +extern void TraceCondemnStart(Trace trace); +extern Res TraceCondemnEnd(double *mortalityReturn, Trace trace); extern Res TraceStart(Trace trace, double mortality, double finishingTime); -extern Size TracePoll(Globals globals); +extern Bool TracePoll(Work *workReturn, Bool *collectWorldReturn, + Globals globals, Bool collectWorldAllowed); extern Rank TraceRankForAccess(Arena arena, Seg seg); extern void TraceSegAccess(Arena arena, Seg seg, AccessSet mode); @@ -419,11 +396,6 @@ extern Bool TraceIdMessagesCheck(Arena arena, TraceId ti); extern Res TraceIdMessagesCreate(Arena arena, TraceId ti); extern void TraceIdMessagesDestroy(Arena arena, TraceId ti); -/* Collection control parameters */ - -extern double TraceWorkFactor; - - /* Equivalent to MPS_SCAN_BEGIN */ #define TRACE_SCAN_BEGIN(ss) \ @@ -482,32 +454,19 @@ extern void TraceScanSingleRef(TraceSet ts, Rank rank, Arena arena, /* Arena Interface -- see */ -/* DEFINE_ARENA_CLASS - * - * Convenience macro -- see . */ - -#define DEFINE_ARENA_CLASS(className, var) \ - DEFINE_ALIAS_CLASS(className, ArenaClass, var) - -#define ARENA_SUPERCLASS(className) \ - ((ArenaClass)SUPERCLASS(className)) - -extern AbstractArenaClass AbstractArenaClassGet(void); -extern Bool ArenaClassCheck(ArenaClass class); +DECLARE_CLASS(Inst, ArenaClass, InstClass); +DECLARE_CLASS(Arena, AbstractArena, Inst); +extern Bool ArenaClassCheck(ArenaClass klass); extern Bool ArenaCheck(Arena arena); -extern Res ArenaCreate(Arena *arenaReturn, ArenaClass class, ArgList args); +extern Res ArenaCreate(Arena *arenaReturn, ArenaClass klass, ArgList args); extern void ArenaDestroy(Arena arena); -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, Count depth); extern Res ArenaDescribeTracts(Arena arena, mps_lib_FILE *stream, Count depth); -extern Bool ArenaAccess(Addr addr, AccessSet mode, MutatorFaultContext context); +extern Bool ArenaAccess(Addr addr, AccessSet mode, MutatorContext 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); @@ -515,13 +474,17 @@ extern Res GlobalsCompleteCreate(Globals arenaGlobals); extern void GlobalsPrepareToDestroy(Globals arenaGlobals); extern Res GlobalsDescribe(Globals arena, mps_lib_FILE *stream, Count depth); extern Ring GlobalsRememberedSummaryRing(Globals); +extern void GlobalsArenaMap(void (*func)(Arena arena)); +extern void GlobalsClaimAll(void); +extern void GlobalsReleaseAll(void); +extern void GlobalsReinitializeAll(void); #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 ArenaEpoch(arena) (ArenaHistory(arena)->epoch) /* .epoch.ts */ #define ArenaTrace(arena, ti) (&(arena)->trace[ti]) #define ArenaZoneShift(arena) ((arena)->zoneShift) #define ArenaStripeSize(arena) ((Size)1 << ArenaZoneShift(arena)) @@ -529,6 +492,9 @@ extern Ring GlobalsRememberedSummaryRing(Globals); #define ArenaGreyRing(arena, rank) (&(arena)->greyRing[rank]) #define ArenaPoolRing(arena) (&ArenaGlobals(arena)->poolRing) #define ArenaChunkTree(arena) RVALUE((arena)->chunkTree) +#define ArenaChunkRing(arena) (&(arena)->chunkRing) +#define ArenaShield(arena) (&(arena)->shieldStruct) +#define ArenaHistory(arena) (&(arena)->historyStruct) extern Bool ArenaGrainSizeCheck(Size size); #define AddrArenaGrainUp(addr, arena) AddrAlignUp(addr, ArenaGrainSize(arena)) @@ -540,16 +506,12 @@ extern Bool ArenaGrainSizeCheck(Size size); 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 ArenaEnter(Arena arena); +extern void ArenaLeave(Arena arena); extern void (ArenaPoll)(Globals globals); #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) AVER(arena->busyTraces == TraceSetEMPTY) #define ArenaPoll(globals) UNUSED(globals) #else #error "No shield configuration." @@ -562,57 +524,53 @@ 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 ArenaPostmortem(Globals globals); 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 ArenaBusy(Arena arena); extern Bool ArenaHasAddr(Arena arena, Addr addr); -extern Res ArenaAddrObject(Addr *pReturn, Arena arena, Addr addr); extern void ArenaChunkInsert(Arena arena, Chunk chunk); extern void ArenaChunkRemoved(Arena arena, Chunk chunk); +extern void ArenaAccumulateTime(Arena arena, Clock start, Clock now); extern void ArenaSetEmergency(Arena arena, Bool emergency); extern Bool ArenaEmergency(Arena arean); extern Res ControlInit(Arena arena); extern void ControlFinish(Arena arena); -extern Res ControlAlloc(void **baseReturn, Arena arena, size_t size, - Bool withReservoirPermit); +extern Res ControlAlloc(void **baseReturn, Arena arena, size_t size); extern void ControlFree(Arena arena, void *base, size_t size); extern Res ControlDescribe(Arena arena, mps_lib_FILE *stream, Count depth); -/* Peek/Poke +/* Peek/Poke/Read/Write -- read/write possibly through barrier * * These are provided so that modules in the MPS can make occasional - * access to client data. They perform the appropriate shield and - * summary manipulations that are necessary. + * access to client data, and to implement a software barrier for + * segments that are not handed out to the mutator. They protect the + * necessary colour, shield and summary invariants. * - * Note that Peek and Poke can be called with address that may or - * may not be in arena managed memory. */ + * Note that Peek and Poke can be called with an address that may or + * may not be in memory managed by arena, whereas Read and Write + * assert this is the case. + */ /* Peek reads a value */ extern Ref ArenaPeek(Arena arena, Ref *p); +/* Same, but p known to be owned by arena */ +extern Ref ArenaRead(Arena arena, Ref *p); /* Same, but p must be in seg */ extern Ref ArenaPeekSeg(Arena arena, Seg seg, Ref *p); /* Poke stores a value */ extern void ArenaPoke(Arena arena, Ref *p, Ref ref); +/* Same, but p known to be owned by arena */ +extern void ArenaWrite(Arena arena, Ref *p, Ref ref); /* Same, but p must be in seg */ extern void ArenaPokeSeg(Arena arena, Seg seg, Ref *p, Ref ref); -/* Read/Write - * - * These simulate mutator reads and writes to locations. - * They are effectively a software barrier, and maintain the tricolor - * invariant (hence performing any scanning or color manipulation - * necessary). - * - * Only Read provided right now. */ - -Ref ArenaRead(Arena arena, Ref *p); - - extern Size ArenaReserved(Arena arena); extern Size ArenaCommitted(Arena arena); extern Size ArenaSpareCommitted(Arena arena); @@ -624,6 +582,8 @@ extern double ArenaSpare(Arena arena); extern Size ArenaCommitLimit(Arena arena); extern Res ArenaSetCommitLimit(Arena arena, Size limit); extern void ArenaSetSpare(Arena arena, double spare); +extern double ArenaPauseTime(Arena arena); +extern void ArenaSetPauseTime(Arena arena, double pauseTime); extern Size ArenaNoPurgeSpare(Arena arena, Size size); extern Res ArenaNoGrow(Arena arena, LocusPref pref, Size size); @@ -637,22 +597,8 @@ extern void ArenaCompact(Arena arena, Trace trace); extern Res ArenaFinalize(Arena arena, Ref obj); extern Res ArenaDefinalize(Arena arena, Ref obj); -#define ArenaReservoir(arena) (&(arena)->reservoirStruct) -#define ReservoirPool(reservoir) (&(reservoir)->poolStruct) - -extern Bool ReservoirCheck(Reservoir reservoir); -extern Res ReservoirInit(Reservoir reservoir, Arena arena); -extern void ReservoirFinish (Reservoir reservoir); -extern Size ReservoirLimit(Reservoir reservoir); -extern void ReservoirSetLimit(Reservoir reservoir, Size size); -extern Size ReservoirAvailable(Reservoir reservoir); -extern Res ReservoirEnsureFull(Reservoir reservoir); -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, LocusPref pref, - Size size, Pool pool, Bool withReservoirPermit); + Size size, Pool pool); extern Res ArenaFreeLandAlloc(Tract *tractReturn, Arena arena, ZoneSet zones, Bool high, Size size, Pool pool); extern void ArenaFree(Addr base, Size size, Pool pool); @@ -664,12 +610,12 @@ extern Res ArenaNoExtend(Arena arena, Addr base, Size size); 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 PolicyShouldCollectWorld(Arena arena, double availableTime, + Clock now, Clock clocks_per_sec); +extern Bool PolicyStartTrace(Trace *traceReturn, Bool *collectWorldReturn, + Arena arena, Bool collectWorldAllowed); extern Bool PolicyPoll(Arena arena); -extern Bool PolicyPollAgain(Arena arena, Clock start, Size tracedSize); +extern Bool PolicyPollAgain(Arena arena, Clock start, Bool moreWork, Work tracedWork); /* Locus interface */ @@ -687,8 +633,8 @@ extern Bool LocusCheck(Arena arena); /* Segment interface */ -extern Res SegAlloc(Seg *segReturn, SegClass class, LocusPref pref, - Size size, Pool pool, Bool withReservoirPermit, +extern Res SegAlloc(Seg *segReturn, SegClass klass, LocusPref pref, + Size size, Pool pool, ArgList args); extern void SegFree(Seg seg); extern Bool SegOfAddr(Seg *segReturn, Arena arena, Addr addr); @@ -697,34 +643,45 @@ extern Bool SegNext(Seg *segReturn, Arena arena, Seg seg); extern Bool SegNextOfRing(Seg *segReturn, Arena arena, Pool pool, Ring next); extern void SegSetWhite(Seg seg, TraceSet white); extern void SegSetGrey(Seg seg, TraceSet grey); +extern void SegFlip(Seg seg, Trace trace); extern void SegSetRankSet(Seg seg, RankSet rankSet); extern void SegSetRankAndSummary(Seg seg, RankSet rankSet, RefSet summary); -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 SegMerge(Seg *mergedSegReturn, Seg segLo, Seg segHi); +extern Res SegSplit(Seg *segLoReturn, Seg *segHiReturn, Seg seg, Addr at); +extern Res SegAccess(Seg seg, Arena arena, Addr addr, + AccessSet mode, MutatorContext context); +extern Res SegWholeAccess(Seg seg, Arena arena, Addr addr, + AccessSet mode, MutatorContext context); +extern Res SegSingleAccess(Seg seg, Arena arena, Addr addr, + AccessSet mode, MutatorContext context); +extern Res SegWhiten(Seg seg, Trace trace); +extern void SegGreyen(Seg seg, Trace trace); +extern void SegBlacken(Seg seg, TraceSet traceSet); +extern Res SegScan(Bool *totalReturn, Seg seg, ScanState ss); +extern Res SegFix(Seg seg, ScanState ss, Addr *refIO); +extern Res SegFixEmergency(Seg seg, ScanState ss, Addr *refIO); +extern void SegReclaim(Seg seg, Trace trace); +extern void SegWalk(Seg seg, Format format, FormattedObjectsVisitor f, + void *v, size_t s); +extern Res SegAbsDescribe(Inst seg, mps_lib_FILE *stream, Count depth); extern Res SegDescribe(Seg seg, mps_lib_FILE *stream, Count depth); extern void SegSetSummary(Seg seg, RefSet summary); -extern Buffer SegBuffer(Seg seg); +extern Bool SegHasBuffer(Seg seg); +extern Bool SegBuffer(Buffer *bufferReturn, Seg seg); extern void SegSetBuffer(Seg seg, Buffer buffer); +extern void SegUnsetBuffer(Seg seg); +extern Bool SegBufferFill(Addr *baseReturn, Addr *limitReturn, + Seg seg, Size size, RankSet rankSet); +extern Addr SegBufferScanLimit(Seg seg); extern Bool SegCheck(Seg seg); extern Bool GCSegCheck(GCSeg gcseg); -extern Bool SegClassCheck(SegClass class); -extern SegClass SegClassGet(void); -extern SegClass GCSegClassGet(void); -extern void SegClassMixInNoSplitMerge(SegClass class); - - -/* DEFINE_SEG_CLASS -- define a segment class */ - -#define DEFINE_SEG_CLASS(className, var) \ - DEFINE_ALIAS_CLASS(className, SegClass, var) - - -#define SEG_SUPERCLASS(className) \ - ((SegClass)SUPERCLASS(className)) - -#define ClassOfSeg(seg) ((seg)->class) +extern Bool SegClassCheck(SegClass klass); +DECLARE_CLASS(Inst, SegClass, InstClass); +DECLARE_CLASS(Seg, Seg, Inst); +DECLARE_CLASS(Seg, GCSeg, Seg); +DECLARE_CLASS(Seg, MutatorSeg, GCSeg); +#define SegGCSeg(seg) MustBeA(GCSeg, (seg)) +extern void SegClassMixInNoSplitMerge(SegClass klass); extern Size SegSize(Seg seg); extern Addr (SegBase)(Seg seg); @@ -735,15 +692,15 @@ extern Addr (SegLimit)(Seg seg); /* .bitfield.promote: The bit field accesses need to be cast to the */ /* right type, otherwise they'll be promoted to signed int, see */ /* standard.ansic.6.2.1.1. */ -#define SegRankSet(seg) ((RankSet)(seg)->rankSet) -#define SegPM(seg) ((AccessSet)(seg)->pm) -#define SegSM(seg) ((AccessSet)(seg)->sm) -#define SegDepth(seg) ((unsigned)(seg)->depth) -#define SegGrey(seg) ((TraceSet)(seg)->grey) -#define SegWhite(seg) ((TraceSet)(seg)->white) -#define SegNailed(seg) ((TraceSet)(seg)->nailed) +#define SegRankSet(seg) RVALUE((RankSet)(seg)->rankSet) +#define SegPM(seg) RVALUE((AccessSet)(seg)->pm) +#define SegSM(seg) RVALUE((AccessSet)(seg)->sm) +#define SegDepth(seg) RVALUE((unsigned)(seg)->depth) +#define SegGrey(seg) RVALUE((TraceSet)(seg)->grey) +#define SegWhite(seg) RVALUE((TraceSet)(seg)->white) +#define SegNailed(seg) RVALUE((TraceSet)(seg)->nailed) #define SegPoolRing(seg) (&(seg)->poolRing) -#define SegOfPoolRing(node) (RING_ELT(Seg, poolRing, (node))) +#define SegOfPoolRing(node) RING_ELT(Seg, poolRing, (node)) #define SegOfGreyRing(node) (&(RING_ELT(GCSeg, greyRing, (node)) \ ->segStruct)) @@ -757,27 +714,25 @@ extern Addr (SegLimit)(Seg seg); /* Buffer Interface -- see */ -extern Res BufferCreate(Buffer *bufferReturn, BufferClass class, +extern Res BufferCreate(Buffer *bufferReturn, BufferClass klass, Pool pool, Bool isMutator, ArgList args); extern void BufferDestroy(Buffer buffer); extern Bool BufferCheck(Buffer buffer); extern Bool SegBufCheck(SegBuf segbuf); extern Res BufferDescribe(Buffer buffer, mps_lib_FILE *stream, Count depth); -extern Res BufferReserve(Addr *pReturn, Buffer buffer, Size size, - Bool withReservoirPermit); +extern Res BufferReserve(Addr *pReturn, Buffer buffer, Size size); /* macro equivalent for BufferReserve, keep in sync with */ /* TODO: Perhaps this isn't really necessary now that we build the MPS with more global optimisation and inlining. RB 2012-09-07 */ -#define BUFFER_RESERVE(pReturn, buffer, size, withReservoirPermit) \ +#define BUFFER_RESERVE(pReturn, buffer, size) \ (AddrAdd(BufferAlloc(buffer), size) > BufferAlloc(buffer) && \ AddrAdd(BufferAlloc(buffer), size) <= (Addr)BufferAP(buffer)->limit ? \ (*(pReturn) = BufferAlloc(buffer), \ BufferAP(buffer)->alloc = AddrAdd(BufferAlloc(buffer), size), \ ResOK) : \ - BufferFill(pReturn, buffer, size, withReservoirPermit)) + BufferFill(pReturn, buffer, size)) -extern Res BufferFill(Addr *pReturn, Buffer buffer, Size size, - Bool withReservoirPermit); +extern Res BufferFill(Addr *pReturn, Buffer buffer, Size size); extern Bool BufferCommit(Buffer buffer, Addr p, Size size); /* macro equivalent for BufferCommit, keep in sync with */ @@ -821,7 +776,6 @@ extern Addr BufferScanLimit(Buffer buffer); extern void BufferReassignSeg(Buffer buffer, Seg seg); extern Bool BufferIsTrapped(Buffer buffer); -extern Bool BufferIsTrappedByMutator(Buffer buffer); extern void BufferRampBegin(Buffer buffer, AllocPattern pattern); extern Res BufferRampEnd(Buffer buffer); @@ -829,22 +783,14 @@ extern void BufferRampReset(Buffer buffer); extern Res BufferFramePush(AllocFrame *frameReturn, Buffer buffer); extern Res BufferFramePop(Buffer buffer, AllocFrame frame); -extern FrameState BufferFrameState(Buffer buffer); -extern void BufferFrameSetState(Buffer buffer, FrameState state); - -/* DEFINE_BUFFER_CLASS -- define a buffer class */ - -#define DEFINE_BUFFER_CLASS(className, var) \ - DEFINE_ALIAS_CLASS(className, BufferClass, var) - -#define BUFFER_SUPERCLASS(className) \ - ((BufferClass)SUPERCLASS(className)) - -extern Bool BufferClassCheck(BufferClass class); -extern BufferClass BufferClassGet(void); -extern BufferClass SegBufClassGet(void); -extern BufferClass RankBufClassGet(void); +extern Bool BufferClassCheck(BufferClass klass); +DECLARE_CLASS(Inst, BufferClass, InstClass); +DECLARE_CLASS(Buffer, Buffer, Inst); +DECLARE_CLASS(Buffer, SegBuf, Buffer); +typedef Buffer RankBuf; +#define RankBufCheck BufferCheck +DECLARE_CLASS(Buffer, RankBuf, SegBuf); extern AllocPattern AllocPatternRamp(void); extern AllocPattern AllocPatternRampCollectAll(void); @@ -922,14 +868,19 @@ extern ZoneSet ZoneSetBlacklist(Arena arena); /* Shield Interface -- see */ +extern void ShieldInit(Shield shield); +extern void ShieldFinish(Shield shield); +extern Bool ShieldCheck(Shield shield); +extern Res ShieldDescribe(Shield shield, mps_lib_FILE *stream, Count depth); +extern void ShieldDestroyQueue(Shield shield, Arena arena); extern void (ShieldRaise)(Arena arena, Seg seg, AccessSet mode); extern void (ShieldLower)(Arena arena, Seg seg, AccessSet mode); extern void (ShieldEnter)(Arena arena); extern void (ShieldLeave)(Arena arena); extern void (ShieldExpose)(Arena arena, Seg seg); extern void (ShieldCover)(Arena arena, Seg seg); -extern void (ShieldSuspend)(Arena arena); -extern void (ShieldResume)(Arena arena); +extern void (ShieldHold)(Arena arena); +extern void (ShieldRelease)(Arena arena); extern void (ShieldFlush)(Arena arena); #if defined(SHIELD) @@ -940,13 +891,13 @@ extern void (ShieldFlush)(Arena arena); #define ShieldLower(arena, seg, mode) \ BEGIN UNUSED(arena); UNUSED(seg); UNUSED(mode); END #define ShieldEnter(arena) BEGIN UNUSED(arena); END -#define ShieldLeave(arena) BEGIN UNUSED(arena); END +#define ShieldLeave(arena) AVER(arena->busyTraces == TraceSetEMPTY) #define ShieldExpose(arena, seg) \ BEGIN UNUSED(arena); UNUSED(seg); END #define ShieldCover(arena, seg) \ BEGIN UNUSED(arena); UNUSED(seg); END -#define ShieldSuspend(arena) BEGIN UNUSED(arena); END -#define ShieldResume(arena) BEGIN UNUSED(arena); END +#define ShieldHold(arena) BEGIN UNUSED(arena); END +#define ShieldRelease(arena) BEGIN UNUSED(arena); END #define ShieldFlush(arena) BEGIN UNUSED(arena); END #else #error "No shield configuration." @@ -955,6 +906,10 @@ extern void (ShieldFlush)(Arena arena); /* Location Dependency -- see */ +extern void HistoryInit(History history); +extern void HistoryFinish(History); +extern Res HistoryDescribe(History history, mps_lib_FILE *stream, Count depth); +extern Bool HistoryCheck(History history); extern void LDReset(mps_ld_t ld, Arena arena); extern void LDAdd(mps_ld_t ld, Arena arena, Addr addr); extern Bool LDIsStaleAny(mps_ld_t ld, Arena arena); @@ -1014,59 +969,64 @@ extern Res RootsIterate(Globals arena, RootIterateFn f, void *p); 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 Size (LandSize)(Land land); +extern Res LandInit(Land land, LandClass klass, Arena arena, Align alignment, void *owner, ArgList args); 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 (LandInsert)(Range rangeReturn, Land land, Range range); +extern Res (LandDelete)(Range rangeReturn, Land land, Range range); +extern Bool (LandIterate)(Land land, LandVisitor visitor, void *closure); +extern Bool (LandIterateAndDelete)(Land land, LandDeleteVisitor visitor, void *closure); +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 Bool LandFlushVisitor(Bool *deleteReturn, Land land, Range range, void *closure); +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()) +extern Bool LandClassCheck(LandClass klass); + +/* See .critical.macros. */ +#define LandSizeMacro(land) Method(Land, land, sizeMethod)(land) +#define LandInsertMacro(rangeReturn, land, range) Method(Land, land, insert)(rangeReturn, land, range) +#define LandDeleteMacro(rangeReturn, land, range) Method(Land, land, delete)(rangeReturn, land, range) +#define LandIterateMacro(land, visitor, closure) Method(Land, land, iterate)(land, visitor, closure) +#define LandIterateAndDeleteMacro(land, visitor, closure) Method(Land, land, iterateAndDelete)(land, visitor, closure) +#define LandFindFirstMacro(rangeReturn, oldRangeReturn, land, size, findDelete) Method(Land, land, findFirst)(rangeReturn, oldRangeReturn, land, size, findDelete) +#define LandFindLastMacro(rangeReturn, oldRangeReturn, land, size, findDelete) Method(Land, land, findLast)(rangeReturn, oldRangeReturn, land, size, findDelete) +#define LandFindLargestMacro(rangeReturn, oldRangeReturn, land, size, findDelete) Method(Land, land, findLargest)(rangeReturn, oldRangeReturn, land, size, findDelete) +#define LandFindInZonesMacro(foundReturn, rangeReturn, oldRangeReturn, land, size, zoneSet, high) Method(Land, land, findInZones)(foundReturn, rangeReturn, oldRangeReturn, land, size, zoneSet, high) +#define LandFlushMacro(dest, src) LandIterateAndDelete(src, LandFlushVisitor, dest) +#if !defined(AVER_AND_CHECK_ALL) +#define LandSize(land) LandSizeMacro(land) +#define LandInsert(rangeReturn, land, range) LandInsertMacro(rangeReturn, land, range) +#define LandDelete(rangeReturn, land, range) LandDeleteMacro(rangeReturn, land, range) +#define LandIterate(land, visitor, closure) LandIterateMacro(land, visitor, closure) +#define LandIterateAndDelete(land, visitor, closure) LandIterateAndDeleteMacro(land, visitor, closure) +#define LandFindFirst(rangeReturn, oldRangeReturn, land, size, findDelete) LandFindFirstMacro(rangeReturn, oldRangeReturn, land, size, findDelete) +#define LandFindLast(rangeReturn, oldRangeReturn, land, size, findDelete) LandFindLastMacro(rangeReturn, oldRangeReturn, land, size, findDelete) +#define LandFindLargest(rangeReturn, oldRangeReturn, land, size, findDelete) LandFindLargestMacro(rangeReturn, oldRangeReturn, land, size, findDelete) +#define LandFindInZones(foundReturn, rangeReturn, oldRangeReturn, land, size, zoneSet, high) LandFindInZonesMacro(foundReturn, rangeReturn, oldRangeReturn, land, size, zoneSet, high) +#define LandFlush(dest, src) LandFlushMacro(dest, src) +#endif /* !defined(AVER_AND_CHECK_ALL) */ + +DECLARE_CLASS(Inst, LandClass, InstClass); +DECLARE_CLASS(Land, Land, Inst); /* STATISTIC -- gather statistics (in some varieties) * - * The argument of STATISTIC is an expression; the expansion followed by - * a semicolon is syntactically a statement. - * - * The argument of STATISTIC_STAT is a statement; the expansion followed by - * a semicolon is syntactically a statement. - * - * STATISTIC_WRITE is inserted in WriteF arguments to output the values - * of statistic fields. - * - * .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 - * passed as part of a comma-expression so that its type is not - * important. This permits an expression of type void. */ + * See . + */ #if defined(STATISTICS) -#define STATISTIC(gather) BEGIN (gather); END -#define STATISTIC_STAT(gather) BEGIN gather; END +#define STATISTIC(gather) BEGIN gather; END #define STATISTIC_WRITE(format, arg) (format), (arg), #elif defined(STATISTICS_NONE) -#define STATISTIC(gather) DISCARD(((gather), 0)) -#define STATISTIC_STAT DISCARD_STAT +#define STATISTIC(gather) NOOP #define STATISTIC_WRITE(format, arg) #else /* !defined(STATISTICS) && !defined(STATISTICS_NONE) */ @@ -1080,7 +1040,7 @@ extern LandClass LandClassGet(void); /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2015 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/mpmss.c b/mps/code/mpmss.c index 2c746b7e34d..792c2e0ff4a 100644 --- a/mps/code/mpmss.c +++ b/mps/code/mpmss.c @@ -1,7 +1,7 @@ /* mpmss.c: MPM STRESS TEST * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. */ @@ -9,7 +9,6 @@ #include "mps.h" #include "mpsavm.h" #include "mpscmfs.h" -#include "mpscmv.h" #include "mpscmvff.h" #include "mpslib.h" #include "mpslib.h" @@ -37,9 +36,9 @@ static void check_allocated_size(mps_pool_t pool, size_t allocated) /* stress -- create a pool of the requested type and allocate in it */ 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) + size_t (*size)(size_t i, mps_align_t align), + 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; @@ -57,11 +56,12 @@ static mps_res_t stress(mps_arena_t arena, mps_pool_debug_option_s *options, /* allocate a load of objects */ for (i=0; i= sizeof(ps[i])) *ps[i] = 1; /* Write something, so it gets swap. */ @@ -83,7 +83,7 @@ static mps_res_t stress(mps_arena_t arena, mps_pool_debug_option_s *options, } /* free half of the objects */ /* upper half, as when allocating them again we want smaller objects */ - /* see randomSize() */ + /* see randomSizeAligned() */ for (i=testSetSIZE/2; i> (i / 10)), 2) + 1; -} - - -/* randomSize8 -- produce sizes both large and small, 8-byte aligned */ - -static size_t randomSize8(size_t 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, 8); + return alignUp(rnd() % max((maxSize >> (i / 10)), 2) + 1, align); } @@ -135,9 +125,10 @@ static size_t randomSize8(size_t i) static size_t fixedSizeSize = 0; -static size_t fixedSize(size_t i) +static size_t fixedSize(size_t i, mps_align_t align) { testlib_unused(i); + testlib_unused(align); return fixedSizeSize; } @@ -158,8 +149,8 @@ static mps_pool_debug_option_s fenceOptions = { /* testInArena -- test all the pool classes in the given arena */ -static void testInArena(mps_arena_class_t arena_class, mps_arg_s *arena_args, - mps_pool_debug_option_s *options) +static void testInArena(mps_arena_class_t arena_class, size_t arena_grain_size, + mps_arg_s *arena_args, mps_pool_debug_option_s *options) { mps_arena_t arena; @@ -167,43 +158,28 @@ static void testInArena(mps_arena_class_t arena_class, mps_arg_s *arena_args, "mps_arena_create"); MPS_ARGS_BEGIN(args) { - mps_align_t align = sizeof(void *) << (rnd() % 4); + mps_align_t align = rnd_align(sizeof(void *), arena_grain_size); 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", + die(stress(arena, NULL, randomSizeAligned, 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_align_t align = rnd_align(sizeof(void *), arena_grain_size); 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", + die(stress(arena, options, randomSizeAligned, 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); @@ -212,24 +188,30 @@ static void testInArena(mps_arena_class_t arena_class, mps_arg_s *arena_args, mps_class_mfs(), args), "stress MFS"); } MPS_ARGS_END(args); + /* Manual allocation should not cause any garbage collections. */ + Insist(mps_collections(arena) == 0); mps_arena_destroy(arena); } int main(int argc, char *argv[]) { + size_t arena_grain_size; + testlib_init(argc, argv); + arena_grain_size = rnd_grain(testArenaSIZE); 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_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, arena_grain_size); + testInArena(mps_arena_class_vm(), arena_grain_size, args, &bothOptions); } MPS_ARGS_END(args); + arena_grain_size = rnd_grain(smallArenaSIZE); 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_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, arena_grain_size); + testInArena(mps_arena_class_vm(), arena_grain_size, args, &fenceOptions); } MPS_ARGS_END(args); printf("%s: Conclusion: Failed to find any defects.\n", argv[0]); @@ -239,7 +221,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2014 Ravenbrook Limited . + * Copyright (c) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/mpmst.h b/mps/code/mpmst.h index 23103762ee9..b66643233c0 100644 --- a/mps/code/mpmst.h +++ b/mps/code/mpmst.h @@ -1,7 +1,7 @@ /* mpmst.h: MEMORY POOL MANAGER DATA STRUCTURES * * $Id$ - * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2001 Global Graphics Software. * * .design: This header file crosses module boundaries. The relevant @@ -36,11 +36,12 @@ * See . * * .class: The pool class structure is defined by each pool class - * implementation in order to provide an interface between the MPM - * and the class (see ) via generic - * functions (see ). A class XXX defines a function - * PoolClassXXX() returning a PoolClass pointing to a PoolClassStruct - * of methods which implement the memory management policy. + * implementation in order to provide an interface between the MPM and + * the class (see ) via generic functions (see + * ). Pool classes use the class protocol (see + * ) and so CLASS(ABCPool) returns a PoolClass + * pointing to a PoolClassStruct of methods which implement the memory + * management policy for pool class ABC. * * .class.end-sig: The class structure has a signature at the end. This * causes the compiler to complain if the class structure is extended @@ -49,41 +50,25 @@ #define PoolClassSig ((Sig)0x519C7A55) /* SIGnature pool CLASS */ typedef struct mps_pool_class_s { - ProtocolClassStruct protocol; - const char *name; /* class name string */ + InstClassStruct instClassStruct; size_t size; /* size of outer structure */ - size_t offset; /* offset of generic struct in outer struct */ Attr attr; /* attributes */ PoolVarargsMethod varargs; /* convert deprecated varargs into keywords */ PoolInitMethod init; /* initialize the pool descriptor */ - PoolFinishMethod finish; /* finish the pool descriptor */ PoolAllocMethod alloc; /* allocate memory from pool */ PoolFreeMethod free; /* free memory to pool */ + PoolSegPoolGenMethod segPoolGen; /* get pool generation of segment */ PoolBufferFillMethod bufferFill; /* out-of-line reserve */ PoolBufferEmptyMethod bufferEmpty; /* out-of-line commit */ - PoolAccessMethod access; /* handles read/write accesses */ - PoolWhitenMethod whiten; /* whiten objects in a segment */ - PoolGreyMethod grey; /* grey non-white objects */ - PoolBlackenMethod blacken; /* blacken grey objects without scanning */ - PoolScanMethod scan; /* find references during tracing */ - PoolFixMethod fix; /* referent reachable during tracing */ - PoolFixEmergencyMethod fixEmergency; /* as fix, no failure allowed */ - PoolReclaimMethod reclaim; /* reclaim dead objects after tracing */ - PoolTraceEndMethod traceEnd; /* do something after all reclaims */ PoolRampBeginMethod rampBegin;/* begin a ramp pattern */ PoolRampEndMethod rampEnd; /* end a ramp pattern */ PoolFramePushMethod framePush; /* push an allocation frame */ PoolFramePopMethod framePop; /* pop an allocation frame */ - PoolFramePopPendingMethod framePopPending; /* notify pending pop */ - PoolAddrObjectMethod addrObject; /* find client pointer to object */ - PoolWalkMethod walk; /* walk over a segment */ PoolFreeWalkMethod freewalk; /* walk over free blocks */ 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; @@ -95,22 +80,23 @@ typedef struct mps_pool_class_s { * a "subclass" of the pool structure (the "outer structure") which * contains PoolStruct as a a field. The outer structure holds the * class-specific part of the pool's state. See , - * . */ + * . + */ #define PoolSig ((Sig)0x519B0019) /* SIGnature POOL */ typedef struct mps_pool_s { /* generic structure */ + InstStruct instStruct; Sig sig; /* */ Serial serial; /* from arena->poolSerial */ - PoolClass class; /* pool class structure */ Arena arena; /* owning arena */ RingStruct arenaRing; /* link in list of pools in arena */ RingStruct bufferRing; /* allocation buffers are attached to pool */ Serial bufferSerial; /* serial of next buffer */ RingStruct segRing; /* segs are attached to pool */ - Align alignment; /* alignment for units */ - Format format; /* format only if class->attr&AttrFMT */ - PoolFixMethod fix; /* fix method */ + Align alignment; /* alignment for grains */ + Shift alignShift; /* log2(alignment) */ + Format format; /* format or NULL */ } PoolStruct; @@ -136,56 +122,11 @@ typedef struct MFSStruct { /* MFS outer structure */ struct MFSHeaderStruct *freeList; /* head of the free list */ Size total; /* total size allocated from arena */ Size free; /* free space in pool */ - Tract tractList; /* the first tract */ + RingStruct extentRing; /* ring of extents in pool */ Sig sig; /* */ } MFSStruct; -/* MVStruct -- MV (Manual Variable) pool outer structure - * - * .mv: See , . - * - * The MV pool outer structure is declared here because it is the - * control pool structure which is inlined in the arena. Normally, - * pool outer structures are declared with the pools. */ - -#define MVSig ((Sig)0x5193B999) /* SIGnature MV */ - -typedef struct MVStruct { /* MV pool outer structure */ - PoolStruct poolStruct; /* generic structure */ - MFSStruct blockPoolStruct; /* for managing block descriptors */ - MFSStruct spanPoolStruct; /* for managing span descriptors */ - Size extendBy; /* segment size to extend pool by */ - Size avgSize; /* client estimate of allocation size */ - Size maxSize; /* client estimate of maximum size */ - Size free; /* free space in pool */ - Size lost; /* */ - RingStruct spans; /* span chain */ - Sig sig; /* */ -} MVStruct; - - -/* ReservoirStruct -- Reservoir structure - * - * .reservoir: See , . - * - * The Reservoir structure is declared here because it is in-lined in - * the arena for storing segments for the low-memory reservoir. It is - * implemented as a pool - but doesn't follow the normal pool naming - * conventions because it's not intended for general use and the use of - * a pool is an incidental detail. */ - -#define ReservoirSig ((Sig)0x5196e599) /* SIGnature REServoir */ - -typedef struct ReservoirStruct { /* Reservoir structure */ - PoolStruct poolStruct; /* generic pool structure */ - Tract reserve; /* linked list of reserve tracts */ - Size reservoirLimit; /* desired reservoir size */ - Size reservoirSize; /* actual reservoir size */ - Sig sig; /* */ -} ReservoirStruct; - - /* MessageClassStruct -- Message Class structure * * See (and , @@ -202,7 +143,7 @@ typedef struct MessageClassStruct { /* generic methods */ MessageDeleteMethod delete; /* terminates a message */ - /* methods specific to MessageTypeFinalization */ + /* methods specific to MessageTypeFINALIZATION */ MessageFinalizationRefMethod finalizationRef; /* methods specific to MessageTypeGC */ @@ -210,7 +151,7 @@ typedef struct MessageClassStruct { MessageGCCondemnedSizeMethod gcCondemnedSize; MessageGCNotCondemnedSizeMethod gcNotCondemnedSize; - /* methods specific to MessageTypeGCStart */ + /* methods specific to MessageTypeGCSTART */ MessageGCStartWhyMethod gcStartWhy; Sig endSig; /* */ @@ -225,7 +166,7 @@ typedef struct MessageClassStruct { typedef struct mps_message_s { Sig sig; /* */ Arena arena; /* owning arena */ - MessageClass class; /* Message Class Structure */ + MessageClass klass; /* Message Class Structure */ Clock postedClock; /* mps_clock() at post time, or 0 */ RingStruct queueRing; /* Message queue ring */ } MessageStruct; @@ -242,21 +183,31 @@ typedef struct mps_message_s { #define SegClassSig ((Sig)0x5195E9C7) /* SIGnature SEG CLass */ typedef struct SegClassStruct { - ProtocolClassStruct protocol; - const char *name; /* class name string */ + InstClassStruct instClassStruct; size_t size; /* size of outer structure */ SegInitMethod init; /* initialize the segment */ - SegFinishMethod finish; /* finish the segment */ SegSetSummaryMethod setSummary; /* set the segment summary */ SegBufferMethod buffer; /* get the segment buffer */ SegSetBufferMethod setBuffer; /* set the segment buffer */ + SegUnsetBufferMethod unsetBuffer; /* unset the segment buffer */ + SegBufferFillMethod bufferFill; /* try filling buffer from segment */ + SegBufferEmptyMethod bufferEmpty; /* empty buffer to segment */ SegSetGreyMethod setGrey; /* change greyness of segment */ + SegFlipMethod flip; /* raise barrier for a flipped trace */ SegSetWhiteMethod setWhite; /* change whiteness of segment */ SegSetRankSetMethod setRankSet; /* change rank set of segment */ SegSetRankSummaryMethod setRankSummary; /* change rank set & summary */ - SegDescribeMethod describe; /* describe the contents of the seg */ SegMergeMethod merge; /* merge two adjacent segments */ SegSplitMethod split; /* split a segment into two */ + SegAccessMethod access; /* handles read/write accesses */ + SegWhitenMethod whiten; /* whiten objects */ + SegGreyenMethod greyen; /* greyen non-white objects */ + SegBlackenMethod blacken; /* blacken grey objects without scanning */ + SegScanMethod scan; /* find references during tracing */ + SegFixMethod fix; /* referent reachable during tracing */ + SegFixMethod fixEmergency; /* as fix, no failure allowed */ + SegReclaimMethod reclaim; /* reclaim dead objects after tracing */ + SegWalkMethod walk; /* walk over a segment */ Sig sig; /* .class.end-sig */ } SegClassStruct; @@ -269,18 +220,20 @@ typedef struct SegClassStruct { #define SegSig ((Sig)0x5195E999) /* SIGnature SEG */ typedef struct SegStruct { /* segment structure */ + InstStruct instStruct; 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 : ShieldDepthWIDTH; /* see */ + unsigned depth : ShieldDepthWIDTH; /* see design.mps.shield.def.depth */ + BOOLFIELD(queued); /* in shield queue? */ 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 */ + unsigned defer : WB_DEFER_BITS; /* defer write barrier for this many scans */ } SegStruct; @@ -296,6 +249,7 @@ typedef struct GCSegStruct { /* GC segment structure */ RingStruct greyRing; /* link in list of grey segs */ RefSet summary; /* summary of references out of seg */ Buffer buffer; /* non-NULL if seg is buffered */ + RingStruct genRing; /* link in list of segs in gen */ Sig sig; /* */ } GCSegStruct; @@ -328,15 +282,12 @@ typedef struct LocusPrefStruct { /* locus placement preferences */ #define BufferClassSig ((Sig)0x519B0FC7) /* SIGnature BUFfer CLass */ typedef struct BufferClassStruct { - ProtocolClassStruct protocol; - const char *name; /* class name string */ + InstClassStruct instClassStruct; size_t size; /* size of outer structure */ BufferVarargsMethod varargs; /* parse obsolete varargs */ BufferInitMethod init; /* initialize the buffer */ - BufferFinishMethod finish; /* finish the buffer */ BufferAttachMethod attach; /* attach the buffer */ BufferDetachMethod detach; /* detach the buffer */ - BufferDescribeMethod describe;/* describe the contents of the buffer */ BufferSegMethod seg; /* seg of buffer */ BufferRankSetMethod rankSet; /* rank set of buffer */ BufferSetRankSetMethod setRankSet; /* change rank set of buffer */ @@ -358,8 +309,8 @@ typedef struct BufferClassStruct { #define BufferSig ((Sig)0x519B0FFE) /* SIGnature BUFFEr */ typedef struct BufferStruct { + InstStruct instStruct; Sig sig; /* */ - BufferClass class; /* buffer class structure */ Serial serial; /* from pool->bufferSerial */ Arena arena; /* owning arena */ Pool pool; /* owning pool */ @@ -406,13 +357,14 @@ typedef struct mps_fmt_s { Serial serial; /* from arena->formatSerial */ Arena arena; /* owning arena */ RingStruct arenaRing; /* formats are attached to the arena */ + Count poolCount; /* number of pools using the format */ Align alignment; /* alignment of formatted objects */ mps_fmt_scan_t scan; mps_fmt_skip_t skip; mps_fmt_fwd_t move; mps_fmt_isfwd_t isMoved; mps_fmt_pad_t pad; - mps_fmt_class_t class; /* pointer indicating class */ + mps_fmt_class_t klass; /* pointer indicating class */ Size headerSize; /* size of header */ } FormatStruct; @@ -425,6 +377,11 @@ typedef struct mps_fmt_s { * through the MPS interface to optimise the critical path scan loop. * See ["The critical path through the MPS"](../design/critical-path.txt). * + * .ss.fix-closure: The fixClosure member allows the caller of the + * scanning protocol to pass data through to this fix function. This + * is not used in the public MPS, but is needed by the transforms + * extension. + * * .ss.zone: For binary compatibility, the zone shift is exported as * a word rather than a shift, so that the external mps_ss_s is a uniform * three-word structure. See and . @@ -445,23 +402,21 @@ typedef struct ScanStateStruct { Sig sig; /* */ struct mps_ss_s ss_s; /* .ss */ Arena arena; /* owning arena */ - PoolFixMethod fix; /* third stage fix function */ - void *fixClosure; /* closure data for fix */ + SegFixMethod fix; /* third stage fix function */ + void *fixClosure; /* see .ss.fix-closure */ TraceSet traces; /* traces to scan for */ Rank rank; /* reference rank of scanning */ Bool wasMarked; /* design.mps.fix.protocol.was-ready */ RefSet fixedSummary; /* accumulated summary of fixed references */ - STATISTIC_DECL(Count fixRefCount); /* refs which pass zone check */ - STATISTIC_DECL(Count segRefCount); /* refs which refer to segs */ - STATISTIC_DECL(Count whiteSegRefCount); /* refs which refer to white segs */ - STATISTIC_DECL(Count nailCount); /* segments nailed by ambig refs */ - STATISTIC_DECL(Count snapCount); /* refs snapped to forwarded objs */ - STATISTIC_DECL(Count forwardedCount); /* objects preserved by moving */ - Size forwardedSize; /* bytes preserved by moving */ - STATISTIC_DECL(Count preservedInPlaceCount); /* objects preserved in place */ - Size preservedInPlaceSize; /* bytes preserved in place */ - STATISTIC_DECL(Size copiedSize); /* bytes copied */ - STATISTIC_DECL(Size scannedSize); /* bytes scanned */ + STATISTIC_DECL(Count fixRefCount) /* refs which pass zone check */ + STATISTIC_DECL(Count segRefCount) /* refs which refer to segs */ + STATISTIC_DECL(Count whiteSegRefCount) /* refs which refer to white segs */ + STATISTIC_DECL(Count nailCount) /* segments nailed by ambig refs */ + STATISTIC_DECL(Count snapCount) /* refs snapped to forwarded objs */ + STATISTIC_DECL(Count forwardedCount) /* objects preserved by moving */ + STATISTIC_DECL(Count preservedInPlaceCount) /* objects preserved in place */ + STATISTIC_DECL(Size copiedSize) /* bytes copied */ + Size scannedSize; /* bytes scanned */ } ScanStateStruct; @@ -479,38 +434,38 @@ typedef struct TraceStruct { TraceState state; /* current state of trace */ Rank band; /* current band */ Bool firstStretch; /* in first stretch of band (see accessor) */ - PoolFixMethod fix; /* fix method to apply to references */ - void *fixClosure; /* closure information for fix method */ - Chain chain; /* chain being incrementally collected */ - STATISTIC_DECL(Size preTraceArenaReserved); /* ArenaReserved before this trace */ + SegFixMethod fix; /* fix method to apply to references */ + void *fixClosure; /* see .ss.fix-closure */ + RingStruct genRing; /* ring of generations condemned for trace */ + STATISTIC_DECL(Size preTraceArenaReserved) /* ArenaReserved before this trace */ Size condemned; /* condemned bytes */ Size notCondemned; /* collectable but not condemned */ Size foundation; /* initial grey set size */ - Size rate; /* segs to scan per increment */ - STATISTIC_DECL(Count greySegCount); /* number of grey segs */ - STATISTIC_DECL(Count greySegMax); /* max number of grey segs */ - STATISTIC_DECL(Count rootScanCount); /* number of roots scanned */ + Work quantumWork; /* tracing work to be done in each poll */ + STATISTIC_DECL(Count greySegCount) /* number of grey segs */ + STATISTIC_DECL(Count greySegMax) /* max number of grey segs */ + STATISTIC_DECL(Count rootScanCount) /* number of roots scanned */ Count rootScanSize; /* total size of scanned roots */ - Size rootCopiedSize; /* bytes copied by scanning roots */ - STATISTIC_DECL(Count segScanCount); /* number of segs scanned */ + STATISTIC_DECL(Size rootCopiedSize) /* bytes copied by scanning roots */ + STATISTIC_DECL(Count segScanCount) /* number of segs scanned */ Count segScanSize; /* total size of scanned segments */ - Size segCopiedSize; /* bytes copied by scanning segments */ - STATISTIC_DECL(Count singleScanCount); /* number of single refs scanned */ - STATISTIC_DECL(Count singleScanSize); /* total size of single refs scanned */ - STATISTIC_DECL(Size singleCopiedSize); /* bytes copied by scanning single refs */ - STATISTIC_DECL(Count fixRefCount); /* refs which pass zone check */ - STATISTIC_DECL(Count segRefCount); /* refs which refer to segs */ - STATISTIC_DECL(Count whiteSegRefCount); /* refs which refer to white segs */ - STATISTIC_DECL(Count nailCount); /* segments nailed by ambig refs */ - STATISTIC_DECL(Count snapCount); /* refs snapped to forwarded objs */ - STATISTIC_DECL(Count readBarrierHitCount); /* read barrier faults */ - STATISTIC_DECL(Count pointlessScanCount); /* pointless seg scans */ - STATISTIC_DECL(Count forwardedCount); /* objects preserved by moving */ + STATISTIC_DECL(Size segCopiedSize) /* bytes copied by scanning segments */ + STATISTIC_DECL(Count singleScanCount) /* number of single refs scanned */ + STATISTIC_DECL(Count singleScanSize) /* total size of single refs scanned */ + STATISTIC_DECL(Size singleCopiedSize) /* bytes copied by scanning single refs */ + STATISTIC_DECL(Count fixRefCount) /* refs which pass zone check */ + STATISTIC_DECL(Count segRefCount) /* refs which refer to segs */ + STATISTIC_DECL(Count whiteSegRefCount) /* refs which refer to white segs */ + STATISTIC_DECL(Count nailCount) /* segments nailed by ambig refs */ + STATISTIC_DECL(Count snapCount) /* refs snapped to forwarded objs */ + STATISTIC_DECL(Count readBarrierHitCount) /* read barrier faults */ + STATISTIC_DECL(Count pointlessScanCount) /* pointless seg scans */ + STATISTIC_DECL(Count forwardedCount) /* objects preserved by moving */ Size forwardedSize; /* bytes preserved by moving */ - STATISTIC_DECL(Count preservedInPlaceCount); /* objects preserved in place */ + STATISTIC_DECL(Count preservedInPlaceCount) /* objects preserved in place */ Size preservedInPlaceSize; /* bytes preserved in place */ - STATISTIC_DECL(Count reclaimCount); /* segments reclaimed */ - STATISTIC_DECL(Count reclaimSize); /* bytes reclaimed */ + STATISTIC_DECL(Count reclaimCount) /* segments reclaimed */ + STATISTIC_DECL(Count reclaimSize) /* bytes reclaimed */ } TraceStruct; @@ -519,13 +474,12 @@ typedef struct TraceStruct { #define ArenaClassSig ((Sig)0x519A6C1A) /* SIGnature ARena CLAss */ typedef struct mps_arena_class_s { - ProtocolClassStruct protocol; - const char *name; /* class name string */ + InstClassStruct instClassStruct; size_t size; /* size of outer structure */ - size_t offset; /* offset of generic struct in outer struct */ ArenaVarargsMethod varargs; ArenaInitMethod init; - ArenaFinishMethod finish; + ArenaCreateMethod create; + ArenaDestroyMethod destroy; ArenaPurgeSpareMethod purgeSpare; ArenaExtendMethod extend; ArenaGrowMethod grow; @@ -533,8 +487,8 @@ typedef struct mps_arena_class_s { ArenaChunkInitMethod chunkInit; ArenaChunkFinishMethod chunkFinish; ArenaCompactMethod compact; - ArenaDescribeMethod describe; ArenaPagesMarkAllocatedMethod pagesMarkAllocated; + ArenaChunkPageMappedMethod chunkPageMapped; Sig sig; } ArenaClassStruct; @@ -574,6 +528,7 @@ typedef struct GlobalsStruct { /* pool fields () */ RingStruct poolRing; /* ring of pools in arena */ Serial poolSerial; /* serial of next created pool */ + Count systemPools; /* count of pools remaining at ArenaDestroy */ /* root fields () */ RingStruct rootRing; /* ring of roots attached to arena */ @@ -598,12 +553,10 @@ typedef struct GlobalsStruct { #define LandClassSig ((Sig)0x5197A4DC) /* SIGnature LAND Class */ typedef struct LandClassStruct { - ProtocolClassStruct protocol; - const char *name; /* class name string */ + InstClassStruct instClassStruct; 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 */ @@ -612,7 +565,6 @@ typedef struct LandClassStruct { 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; @@ -625,8 +577,8 @@ typedef struct LandClassStruct { #define LandSig ((Sig)0x5197A4D9) /* SIGnature LAND */ typedef struct LandStruct { + InstStruct instStruct; Sig sig; /* */ - LandClass class; /* land class structure */ Arena arena; /* owning arena */ Align alignment; /* alignment of addresses */ Bool inLand; /* prevent reentrance */ @@ -646,13 +598,13 @@ typedef struct LandStruct { typedef struct CBSStruct { LandStruct landStruct; /* superclass fields come first */ SplayTreeStruct splayTreeStruct; - STATISTIC_DECL(Count treeSize); + 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); + METER_DECL(treeSearch) Sig sig; /* .class.end-sig */ } CBSStruct; @@ -696,29 +648,110 @@ typedef struct FreelistStruct { } FreelistStruct; +/* SortStruct -- extra memory required by sorting + * + * See QuickSort in mpm.c. This exists so that the caller can make + * the choice about where to allocate the memory, since the MPS has to + * operate in tight stack constraints -- see design.mps.sp. + */ + +typedef struct SortStruct { + struct { + Index left, right; + } stack[MPS_WORD_WIDTH]; +} SortStruct; + + +/* ShieldStruct -- per-arena part of the shield + * + * See design.mps.shield, impl.c.shield. + */ + +#define ShieldSig ((Sig)0x519581E1) /* SIGnature SHEILd */ + +typedef struct ShieldStruct { + Sig sig; /* design.mps.sig */ + BOOLFIELD(inside); /* design.mps.shield.def.inside */ + BOOLFIELD(suspended); /* mutator suspended? */ + BOOLFIELD(queuePending); /* queue insertion pending? */ + Seg *queue; /* queue of unsynced segs */ + Count length; /* number of elements in shield queue */ + Index next; /* next free element in shield queue */ + Index limit; /* high water mark for cache usage */ + Count depth; /* sum of depths of all segs */ + Count unsynced; /* number of unsynced segments */ + Count holds; /* number of holds */ + SortStruct sortStruct; /* workspace for queue sort */ +} ShieldStruct; + + +/* History -- location dependency history + * + * See design.mps.arena.ld. + */ + +#define HistorySig ((Sig)0x51981520) /* SIGnature HISTOry */ + +typedef struct HistoryStruct { + Sig sig; /* design.mps.sig */ + Epoch epoch; /* */ + RefSet prehistory; /* */ + RefSet history[LDHistoryLENGTH]; /* */ +} HistoryStruct; + + +/* MVFFStruct -- MVFF (Manual Variable First Fit) pool outer structure + * + * The signature is placed at the end, see + * + * + * The MVFF pool outer structure is declared here because it is the + * control pool structure which is inlined in the arena. Normally, + * pool outer structures are declared with the pools. + */ + +#define MVFFSig ((Sig)0x5193FFF9) /* SIGnature MVFF */ + +typedef struct MVFFStruct { /* MVFF pool outer structure */ + PoolStruct poolStruct; /* generic structure */ + LocusPrefStruct locusPrefStruct; /* the preferences for allocation */ + Size extendBy; /* size to extend pool by */ + Size avgSize; /* client estimate of allocation size */ + 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; + + /* ArenaStruct -- generic arena * - * See . */ + * See . + */ #define ArenaSig ((Sig)0x519A6E4A) /* SIGnature ARENA */ typedef struct mps_arena_s { + InstStruct instStruct; + GlobalsStruct globals; /* must be first, see */ Serial serial; - ArenaClass class; /* arena class structure */ - Bool poolReady; /* */ - MVStruct controlPoolStruct; /* */ - - ReservoirStruct reservoirStruct; /* */ + MVFFStruct controlPoolStruct; /* */ 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 spareCommitted; /* amount of memory in hysteresis fund */ double spare; /* limit on spareCommitted */ + double pauseTime; /* maximum pause time, in seconds */ Shift zoneShift; /* see also */ Size grainSize; /* */ @@ -757,15 +790,9 @@ typedef struct mps_arena_s { RingStruct threadRing; /* ring of attached threads */ RingStruct deadRing; /* ring of dead threads */ Serial threadSerial; /* serial of next thread */ - - /* shield fields () */ - Bool insideShield; /* TRUE if and only if inside shield */ - Seg shCache[ShieldCacheSIZE]; /* Cache of unsynced segs */ - Size shCacheI; /* index into cache */ - Size shCacheLimit; /* High water mark for cache usage */ - Size shDepth; /* sum of depths of all segs */ - Bool suspended; /* TRUE iff mutator suspended */ + ShieldStruct shieldStruct; + /* trace fields () */ TraceSet busyTraces; /* set of running traces */ TraceSet flippedTraces; /* set of running and flipped traces */ @@ -777,23 +804,21 @@ typedef struct mps_arena_s { TraceMessage tMessage[TraceLIMIT]; /* */ /* policy fields */ - double tracedSize; + double tracedWork; double tracedTime; Clock lastWorldCollect; RingStruct greyRing[RankLIMIT]; /* ring of grey segments at each rank */ - STATISTIC_DECL(Count writeBarrierHitCount); /* write barrier hits */ + STATISTIC_DECL(Count writeBarrierHitCount) /* write barrier hits */ RingStruct chainRing; /* ring of chains */ - /* location dependency fields () */ - Epoch epoch; /* */ - RefSet prehistory; /* */ - RefSet history[LDHistoryLENGTH]; /* */ - + struct HistoryStruct historyStruct; + Bool emergency; /* garbage collect in emergency mode? */ - Word *stackAtArenaEnter; /* NULL or hot end of client stack, in the thread */ - /* that then entered the MPS. */ + /* Stack scanning -- see design.mps.stack-scan */ + void *stackWarm; /* NULL or stack pointer warmer than + mutator state. */ Sig sig; } ArenaStruct; @@ -809,7 +834,7 @@ typedef struct AllocPatternStruct { /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2016 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/mpmtypes.h b/mps/code/mpmtypes.h index 2571106984a..ce21bc5b3ab 100644 --- a/mps/code/mpmtypes.h +++ b/mps/code/mpmtypes.h @@ -1,7 +1,7 @@ /* mpmtypes.h: MEMORY POOL MANAGER TYPES * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * Portions copyright (c) 2001 Global Graphics Software. * * .design: @@ -38,6 +38,7 @@ typedef Word Size; /* */ typedef Word Count; /* */ typedef Word Index; /* */ typedef Word Align; /* */ +typedef Word Work; /* */ typedef unsigned Shift; /* */ typedef unsigned Serial; /* */ typedef Addr Ref; /* */ @@ -61,26 +62,19 @@ typedef unsigned TraceSet; /* */ typedef unsigned TraceState; /* */ typedef unsigned AccessSet; /* */ typedef unsigned Attr; /* */ -typedef int RootVar; /* */ +typedef unsigned RootVar; /* */ typedef Word *BT; /* */ typedef struct BootBlockStruct *BootBlock; /* */ typedef struct BufferStruct *Buffer; /* */ typedef struct SegBufStruct *SegBuf; /* */ typedef struct BufferClassStruct *BufferClass; /* */ -typedef BufferClass SegBufClass; /* */ -typedef BufferClass RankBufClass; /* */ typedef unsigned BufferMode; /* */ -typedef unsigned FrameState; /* */ typedef struct mps_fmt_s *Format; /* design.mps.format */ typedef struct LockStruct *Lock; /* * */ typedef struct mps_pool_s *Pool; /* */ +typedef Pool AbstractPool; typedef struct mps_pool_class_s *PoolClass; /* */ -typedef PoolClass AbstractPoolClass; /* */ -typedef PoolClass AbstractBufferPoolClass; /* */ -typedef PoolClass AbstractSegBufPoolClass; /* */ -typedef PoolClass AbstractScanPoolClass; /* */ -typedef PoolClass AbstractCollectPoolClass; /* */ typedef struct TraceStruct *Trace; /* */ typedef struct ScanStateStruct *ScanState; /* */ typedef struct mps_chain_s *Chain; /* */ @@ -91,35 +85,36 @@ typedef union PageUnion *Page; /* */ typedef struct SegStruct *Seg; /* */ typedef struct GCSegStruct *GCSeg; /* */ typedef struct SegClassStruct *SegClass; /* */ -typedef SegClass GCSegClass; /* */ typedef struct LocusPrefStruct *LocusPref; /* , */ -typedef int LocusPrefKind; /* , */ +typedef unsigned LocusPrefKind; /* , */ typedef struct mps_arena_class_s *ArenaClass; /* */ -typedef ArenaClass AbstractArenaClass; /* */ typedef struct mps_arena_s *Arena; /* */ +typedef Arena AbstractArena; typedef struct GlobalsStruct *Globals; /* */ typedef struct VMStruct *VM; /* * */ typedef struct RootStruct *Root; /* */ typedef struct mps_thr_s *Thread; /* * */ -typedef struct MutatorFaultContextStruct - *MutatorFaultContext; /* */ +typedef struct MutatorContextStruct *MutatorContext; /* */ typedef struct PoolDebugMixinStruct *PoolDebugMixin; typedef struct AllocPatternStruct *AllocPattern; typedef struct AllocFrameStruct *AllocFrame; /* */ -typedef struct ReservoirStruct *Reservoir; /* */ typedef struct StackContextStruct *StackContext; typedef struct RangeStruct *Range; /* */ +typedef struct RangeTreeStruct *RangeTree; typedef struct LandStruct *Land; /* */ typedef struct LandClassStruct *LandClass; /* */ typedef unsigned FindDelete; /* */ +typedef struct ShieldStruct *Shield; /* design.mps.shield */ +typedef struct HistoryStruct *History; /* design.mps.arena.ld */ +typedef struct PoolGenStruct *PoolGen; /* */ /* Arena*Method -- see */ typedef void (*ArenaVarargsMethod)(ArgStruct args[], va_list varargs); -typedef Res (*ArenaInitMethod)(Arena *arenaReturn, - ArenaClass class, ArgList args); -typedef void (*ArenaFinishMethod)(Arena arena); +typedef Res (*ArenaCreateMethod)(Arena *arenaReturn, ArgList args); +typedef void (*ArenaDestroyMethod)(Arena arena); +typedef Res (*ArenaInitMethod)(Arena arena, Size grainSize, ArgList args); typedef Size (*ArenaPurgeSpareMethod)(Arena arena, Size size); typedef Res (*ArenaExtendMethod)(Arena arena, Addr base, Size size); typedef Res (*ArenaGrowMethod)(Arena arena, LocusPref pref, Size size); @@ -127,10 +122,10 @@ 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, Count depth); typedef Res (*ArenaPagesMarkAllocatedMethod)(Arena arena, Chunk chunk, Index baseIndex, Count pages, Pool pool); +typedef Bool (*ArenaChunkPageMappedMethod)(Chunk chunk, Index index); /* These are not generally exposed and public, but are part of a commercial @@ -157,29 +152,40 @@ typedef void (*FreeBlockVisitor)(Addr base, Addr limit, Pool pool, void *p); /* Seg*Method -- see */ typedef Res (*SegInitMethod)(Seg seg, Pool pool, Addr base, Size size, - Bool withReservoirPermit, ArgList args); -typedef void (*SegFinishMethod)(Seg seg); + ArgList args); typedef void (*SegSetGreyMethod)(Seg seg, TraceSet grey); +typedef void (*SegFlipMethod)(Seg seg, Trace trace); typedef void (*SegSetWhiteMethod)(Seg seg, TraceSet white); typedef void (*SegSetRankSetMethod)(Seg seg, RankSet rankSet); typedef void (*SegSetRankSummaryMethod)(Seg seg, RankSet rankSet, RefSet summary); typedef void (*SegSetSummaryMethod)(Seg seg, RefSet summary); -typedef Buffer (*SegBufferMethod)(Seg seg); +typedef Bool (*SegBufferMethod)(Buffer *bufferReturn, Seg seg); typedef void (*SegSetBufferMethod)(Seg seg, Buffer buffer); -typedef Res (*SegDescribeMethod)(Seg seg, mps_lib_FILE *stream, Count depth); +typedef void (*SegUnsetBufferMethod)(Seg seg); +typedef Bool (*SegBufferFillMethod)(Addr *baseReturn, Addr *limitReturn, + Seg seg, Size size, RankSet rankSet); +typedef void (*SegBufferEmptyMethod)(Seg seg, Buffer buffer); typedef Res (*SegMergeMethod)(Seg seg, Seg segHi, - Addr base, Addr mid, Addr limit, - Bool withReservoirPermit); + Addr base, Addr mid, Addr limit); typedef Res (*SegSplitMethod)(Seg seg, Seg segHi, - Addr base, Addr mid, Addr limit, - Bool withReservoirPermit); + Addr base, Addr mid, Addr limit); +typedef Res (*SegAccessMethod)(Seg seg, Arena arena, Addr addr, + AccessSet mode, MutatorContext context); +typedef Res (*SegWhitenMethod)(Seg seg, Trace trace); +typedef void (*SegGreyenMethod)(Seg seg, Trace trace); +typedef void (*SegBlackenMethod)(Seg seg, TraceSet traceSet); +typedef Res (*SegScanMethod)(Bool *totalReturn, Seg seg, ScanState ss); +typedef Res (*SegFixMethod)(Seg seg, ScanState ss, Ref *refIO); +typedef void (*SegReclaimMethod)(Seg seg, Trace trace); +typedef void (*SegWalkMethod)(Seg seg, Format format, FormattedObjectsVisitor f, + void *v, size_t s); + /* Buffer*Method -- see */ typedef void (*BufferVarargsMethod)(ArgStruct args[], va_list varargs); -typedef Res (*BufferInitMethod)(Buffer buffer, Pool pool, ArgList args); -typedef void (*BufferFinishMethod)(Buffer buffer); +typedef Res (*BufferInitMethod)(Buffer buffer, Pool pool, Bool isMutator, ArgList args); typedef void (*BufferAttachMethod)(Buffer buffer, Addr base, Addr limit, Addr init, Size size); typedef void (*BufferDetachMethod)(Buffer buffer); @@ -187,53 +193,28 @@ 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, Count depth); -/* Pool*Method -- see */ +/* Pool*Method -- see */ /* Order of types corresponds to PoolClassStruct in */ typedef void (*PoolVarargsMethod)(ArgStruct args[], va_list varargs); -typedef Res (*PoolInitMethod)(Pool pool, ArgList args); -typedef void (*PoolFinishMethod)(Pool pool); -typedef Res (*PoolAllocMethod)(Addr *pReturn, Pool pool, Size size, - Bool withReservoirPermit); +typedef Res (*PoolInitMethod)(Pool pool, Arena arena, PoolClass klass, ArgList args); +typedef Res (*PoolAllocMethod)(Addr *pReturn, Pool pool, Size size); typedef void (*PoolFreeMethod)(Pool pool, Addr old, Size size); +typedef PoolGen (*PoolSegPoolGenMethod)(Pool pool, Seg seg); typedef Res (*PoolBufferFillMethod)(Addr *baseReturn, Addr *limitReturn, - Pool pool, Buffer buffer, Size size, - Bool withReservoirPermit); -typedef void (*PoolBufferEmptyMethod)(Pool pool, Buffer buffer, - Addr init, Addr limit); -typedef Res (*PoolTraceBeginMethod)(Pool pool, Trace trace); -typedef Res (*PoolAccessMethod)(Pool pool, Seg seg, Addr addr, - AccessSet mode, MutatorFaultContext context); -typedef Res (*PoolWhitenMethod)(Pool pool, Trace trace, Seg seg); -typedef void (*PoolGreyMethod)(Pool pool, Trace trace, Seg seg); -typedef void (*PoolBlackenMethod)(Pool pool, TraceSet traceSet, Seg seg); -typedef Res (*PoolScanMethod)(Bool *totalReturn, ScanState ss, - Pool pool, Seg seg); -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 void (*PoolTraceEndMethod)(Pool pool, Trace trace); + Pool pool, Buffer buffer, Size size); +typedef void (*PoolBufferEmptyMethod)(Pool pool, Buffer buffer); typedef void (*PoolRampBeginMethod)(Pool pool, Buffer buf, Bool collectAll); typedef void (*PoolRampEndMethod)(Pool pool, Buffer buf); typedef Res (*PoolFramePushMethod)(AllocFrame *frameReturn, Pool pool, Buffer buf); typedef Res (*PoolFramePopMethod)(Pool pool, Buffer buf, AllocFrame frame); -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, FormattedObjectsVisitor f, - void *v, size_t s); typedef void (*PoolFreeWalkMethod)(Pool pool, FreeBlockVisitor f, void *p); typedef BufferClass (*PoolBufferClassMethod)(void); -typedef Res (*PoolDescribeMethod)(Pool pool, mps_lib_FILE *stream, Count depth); typedef PoolDebugMixin (*PoolDebugMixinMethod)(Pool pool); typedef Size (*PoolSizeMethod)(Pool pool); @@ -265,25 +246,23 @@ typedef struct TraceMessageStruct *TraceMessage; /* trace end */ /* Land*Method -- see */ -typedef Res (*LandInitMethod)(Land land, ArgList args); -typedef void (*LandFinishMethod)(Land land); +typedef Res (*LandInitMethod)(Land land, Arena arena, Align alignment, ArgList args); 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 (*LandVisitor)(Land land, Range range, void *closure); +typedef Bool (*LandDeleteVisitor)(Bool *deleteReturn, Land land, Range range, void *closure); +typedef Bool (*LandIterateMethod)(Land land, LandVisitor visitor, void *closure); +typedef Bool (*LandIterateAndDeleteMethod)(Land land, LandDeleteVisitor visitor, void *closure); 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 */ -/* SIGnature IS BAD */ -#define SigInvalid ((Sig)0x51915BAD) +/* */ +#define SigInvalid ((Sig)0x51915BAD) /* SIGnature IS BAD */ #define SizeMAX ((Size)-1) #define AccessSetEMPTY ((AccessSet)0) /* */ @@ -294,14 +273,14 @@ typedef Res (*LandDescribeMethod)(Land land, mps_lib_FILE *stream, Count depth); #define RefSetUNIV BS_UNIV(RefSet) #define ZoneSetEMPTY BS_EMPTY(ZoneSet) #define ZoneSetUNIV BS_UNIV(ZoneSet) +#define ZoneShiftUNSET ((Shift)-1) #define TraceSetEMPTY BS_EMPTY(TraceSet) #define TraceSetUNIV ((TraceSet)((1u << TraceLIMIT) - 1)) #define RankSetEMPTY BS_EMPTY(RankSet) #define RankSetUNIV ((RankSet)((1u << RankLIMIT) - 1)) -#define AttrFMT ((Attr)(1<<0)) /* */ -#define AttrGC ((Attr)(1<<1)) -#define AttrMOVINGGC ((Attr)(1<<2)) -#define AttrMASK (AttrFMT | AttrGC | AttrMOVINGGC) +#define AttrGC ((Attr)(1<<0)) +#define AttrMOVINGGC ((Attr)(1<<1)) +#define AttrMASK (AttrGC | AttrMOVINGGC) /* Locus preferences */ @@ -320,14 +299,6 @@ enum { #define BufferModeTRANSITION ((BufferMode)(1<<3)) -/* Buffer frame states. See */ -enum { - BufferFrameVALID = 1, - BufferFramePOP_PENDING, - BufferFrameDISABLED -}; - - /* Rank constants -- see */ /* These definitions must match . */ /* This is checked by . */ @@ -443,14 +414,13 @@ typedef double WriteFD; /* STATISTIC_DECL -- declare a field to accumulate statistics in * * The argument is a field declaration (a struct-declaration minus the - * semicolon) for a single field (no commas). Currently, we always - * leave them in, see design.mps.metrics. + * semicolon) for a single field (no commas). */ #if defined(STATISTICS) -#define STATISTIC_DECL(field) field +#define STATISTIC_DECL(field) field; #elif defined(STATISTICS_NONE) -#define STATISTIC_DECL(field) field +#define STATISTIC_DECL(field) #else #error "No statistics configured." #endif @@ -461,7 +431,7 @@ typedef double WriteFD; /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/mps.c b/mps/code/mps.c index 48028242a92..f11381213a5 100644 --- a/mps/code/mps.c +++ b/mps/code/mps.c @@ -1,14 +1,14 @@ /* mps.c: MEMORY POOL SYSTEM ALL-IN-ONE TRANSLATION UNIT * * $Id$ - * Copyright (C) 2012-2016 Ravenbrook Limited. See end of file for license. + * Copyright (C) 2012-2018 Ravenbrook Limited. See end of file for license. * * .purpose: This file can be compiled to create the complete MPS library in * a single compilation, allowing the compiler to apply global optimizations * and inlining effectively. On most modern compilers this is also faster * than compiling each file separately. * - * .purpose.universal: This file also allows simple building of a Mac OS X + * .purpose.universal: This file also allows simple building of a macOS * "universal" (multiple architecture) binary when the set of source files * differs by architecture. It may work for other platforms in a similar * manner. @@ -39,7 +39,6 @@ #include "locus.c" #include "tract.c" #include "walk.c" -#include "reserv.c" #include "protocol.c" #include "pool.c" #include "poolabs.c" @@ -60,12 +59,12 @@ #include "message.c" #include "poolmrg.c" #include "poolmfs.c" -#include "poolmv.c" #include "dbgpool.c" #include "dbgpooli.c" #include "boot.c" #include "meter.c" #include "tree.c" +#include "rangetree.c" #include "splay.c" #include "cbs.c" #include "ss.c" @@ -107,39 +106,39 @@ #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 "prmcan.c" /* generic operating system mutator context */ +#include "prmcanan.c" /* generic architecture 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 */ +/* macOS on IA-32 built with Clang or GCC */ #elif defined(MPS_PF_XCI3LL) || defined(MPS_PF_XCI3GC) #include "lockix.c" /* Posix locks */ -#include "thxc.c" /* OS X Mach threading */ +#include "thxc.c" /* macOS Mach threading */ #include "vmix.c" /* Posix virtual memory */ #include "protix.c" /* Posix protection */ -#include "protxc.c" /* OS X Mach exception handling */ -#include "proti3.c" /* 32-bit Intel mutator context decoding */ -#include "prmci3xc.c" /* 32-bit Intel for Mac OS X mutator context */ +#include "protxc.c" /* macOS Mach exception handling */ +#include "prmci3.c" /* IA-32 mutator context */ +#include "prmcxc.c" /* macOS mutator context */ +#include "prmcxci3.c" /* IA-32 for macOS mutator context */ #include "span.c" /* generic stack probe */ -#include "ssixi3.c" /* Posix on 32-bit Intel stack scan */ -/* Mac OS X on 64-bit Intel build with Clang or GCC */ +/* macOS on x86-64 build with Clang or GCC */ #elif defined(MPS_PF_XCI6LL) || defined(MPS_PF_XCI6GC) #include "lockix.c" /* Posix locks */ -#include "thxc.c" /* OS X Mach threading */ +#include "thxc.c" /* macOS Mach threading */ #include "vmix.c" /* Posix virtual memory */ #include "protix.c" /* Posix protection */ -#include "protxc.c" /* OS X Mach exception handling */ -#include "proti6.c" /* 64-bit Intel mutator context decoding */ -#include "prmci6xc.c" /* 64-bit Intel for Mac OS X mutator context */ +#include "protxc.c" /* macOS Mach exception handling */ +#include "prmci6.c" /* x86-64 mutator context */ +#include "prmcxc.c" /* macOS mutator context */ +#include "prmcxci6.c" /* x86-64 for macOS mutator context */ #include "span.c" /* generic stack probe */ -#include "ssixi6.c" /* Posix on 64-bit Intel stack scan */ -/* FreeBSD on 32-bit Intel built with GCC or Clang */ +/* FreeBSD on IA-32 built with GCC or Clang */ #elif defined(MPS_PF_FRI3GC) || defined(MPS_PF_FRI3LL) @@ -149,12 +148,12 @@ #include "vmix.c" /* Posix virtual memory */ #include "protix.c" /* Posix protection */ #include "protsgix.c" /* Posix signal handling */ -#include "prmcan.c" /* generic mutator context */ -#include "prmci3fr.c" /* 32-bit Intel for FreeBSD mutator context */ +#include "prmcanan.c" /* generic architecture mutator context */ +#include "prmcix.c" /* Posix mutator context */ +#include "prmcfri3.c" /* IA-32 for FreeBSD mutator context */ #include "span.c" /* generic stack probe */ -#include "ssixi3.c" /* Posix on 32-bit Intel stack scan */ -/* FreeBSD on 64-bit Intel built with GCC or Clang */ +/* FreeBSD on x86-64 built with GCC or Clang */ #elif defined(MPS_PF_FRI6GC) || defined(MPS_PF_FRI6LL) @@ -164,99 +163,67 @@ #include "vmix.c" /* Posix virtual memory */ #include "protix.c" /* Posix protection */ #include "protsgix.c" /* Posix signal handling */ -#include "prmcan.c" /* generic mutator context */ -#include "prmci6fr.c" /* 64-bit Intel for FreeBSD mutator context */ +#include "prmcanan.c" /* generic architecture mutator context */ +#include "prmcix.c" /* Posix mutator context */ +#include "prmcfri6.c" /* x86-64 for FreeBSD mutator context */ #include "span.c" /* generic stack probe */ -#include "ssixi6.c" /* Posix on 64-bit Intel stack scan */ -/* Linux on 32-bit Intel with GCC */ +/* Linux on IA-32 with GCC */ #elif defined(MPS_PF_LII3GC) -#include "lockli.c" /* Linux locks */ +#include "lockix.c" /* Posix 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 "proti3.c" /* 32-bit Intel mutator context */ -#include "prmci3li.c" /* 32-bit Intel for Linux mutator context */ +#include "protsgix.c" /* Posix signal handling */ +#include "prmci3.c" /* IA-32 mutator context */ +#include "prmcix.c" /* Posix mutator context */ +#include "prmclii3.c" /* IA-32 for Linux mutator context */ #include "span.c" /* generic stack probe */ -#include "ssixi3.c" /* Posix on 32-bit Intel stack scan */ -/* Linux on 64-bit Intel with GCC or Clang */ +/* Linux on x86-64 with GCC or Clang */ #elif defined(MPS_PF_LII6GC) || defined(MPS_PF_LII6LL) -#include "lockli.c" /* Linux locks */ +#include "lockix.c" /* Posix 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 "protsgix.c" /* Posix signal handling */ +#include "prmci6.c" /* x86-64 mutator context */ +#include "prmcix.c" /* Posix mutator context */ +#include "prmclii6.c" /* x86-64 for Linux mutator context */ #include "span.c" /* generic stack probe */ -#include "ssixi6.c" /* Posix on 64-bit Intel stack scan */ -/* Windows on 32-bit Intel with Microsoft Visual Studio */ +/* Windows on IA-32 with Microsoft Visual Studio or Pelles C */ -#elif defined(MPS_PF_W3I3MV) +#elif defined(MPS_PF_W3I3MV) || 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 "ssw3i3mv.c" /* Windows on 32-bit Intel stack scan for Microsoft C */ -#include "spw3i3.c" /* Windows on 32-bit Intel stack probe */ +#include "prmci3.c" /* IA-32 mutator context */ +#include "prmcw3.c" /* Windows mutator context */ +#include "prmcw3i3.c" /* Windows on IA-32 mutator context */ +#include "spw3i3.c" /* Windows on IA-32 stack probe */ #include "mpsiw3.c" /* Windows interface layer extras */ -/* Windows on 64-bit Intel with Microsoft Visual Studio */ +/* Windows on x86-64 with Microsoft Visual Studio or Pelles C */ -#elif defined(MPS_PF_W3I6MV) +#elif defined(MPS_PF_W3I6MV) || 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 "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 "prmci6.c" /* x86-64 mutator context */ +#include "prmcw3.c" /* Windows mutator context */ +#include "prmcw3i6.c" /* Windows on x86-64 mutator context */ +#include "spw3i6.c" /* Windows on x86-64 stack probe */ #include "mpsiw3.c" /* Windows interface layer extras */ #else @@ -269,7 +236,7 @@ /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2012-2016 Ravenbrook Limited . + * Copyright (C) 2012-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/mps.h b/mps/code/mps.h index 0719a80e339..70a9561a513 100644 --- a/mps/code/mps.h +++ b/mps/code/mps.h @@ -189,6 +189,9 @@ extern const struct mps_key_s _mps_key_COMMIT_LIMIT; 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_PAUSE_TIME; +#define MPS_KEY_PAUSE_TIME (&_mps_key_PAUSE_TIME) +#define MPS_KEY_PAUSE_TIME_FIELD d extern const struct mps_key_s _mps_key_EXTEND_BY; #define MPS_KEY_EXTEND_BY (&_mps_key_EXTEND_BY) @@ -318,9 +321,6 @@ typedef struct mps_ap_s { /* allocation point descriptor */ mps_addr_t init; /* limit of initialized memory */ mps_addr_t alloc; /* limit of allocated memory */ mps_addr_t limit; /* limit of available memory */ - mps_addr_t _frameptr; /* lightweight frame pointer */ - mps_bool_t _enabled; /* lightweight frame status */ - mps_bool_t _lwpoppending; /* lightweight pop pending? */ } mps_ap_s; @@ -435,6 +435,7 @@ typedef struct mps_fmt_fixed_s { extern void mps_arena_clamp(mps_arena_t); extern void mps_arena_release(mps_arena_t); extern void mps_arena_park(mps_arena_t); +extern void mps_arena_postmortem(mps_arena_t); extern void mps_arena_expose(mps_arena_t); extern void mps_arena_unsafe_expose_remember_protection(mps_arena_t); extern void mps_arena_unsafe_restore_protection(mps_arena_t); @@ -459,6 +460,10 @@ extern void mps_arena_spare_set(mps_arena_t, double); extern void mps_arena_spare_commit_limit_set(mps_arena_t, size_t); extern size_t mps_arena_spare_commit_limit(mps_arena_t); +extern double mps_arena_pause_time(mps_arena_t); +extern void mps_arena_pause_time_set(mps_arena_t, double); + +extern mps_bool_t mps_arena_busy(mps_arena_t); extern mps_bool_t mps_arena_has_addr(mps_arena_t, mps_addr_t); extern mps_bool_t mps_addr_pool(mps_pool_t *, mps_arena_t, mps_addr_t); extern mps_bool_t mps_addr_fmt(mps_fmt_t *, mps_arena_t, mps_addr_t); @@ -529,6 +534,8 @@ extern mps_res_t (mps_reserve)(mps_addr_t *, mps_ap_t, size_t); extern mps_bool_t (mps_commit)(mps_ap_t, mps_addr_t, size_t); extern mps_res_t mps_ap_fill(mps_addr_t *, mps_ap_t, size_t); + +/* mps_ap_fill_with_reservoir_permit is deprecated */ extern mps_res_t mps_ap_fill_with_reservoir_permit(mps_addr_t *, mps_ap_t, size_t); @@ -611,7 +618,7 @@ extern void mps_sac_empty(mps_sac_t, mps_addr_t, size_t); #define MPS_SAC_FREE(sac, p, size) MPS_SAC_FREE_FAST(sac, p, size) -/* Low memory reservoir */ +/* Low memory reservoir (deprecated) */ extern void mps_reservoir_limit_set(mps_arena_t, size_t); extern size_t mps_reservoir_limit(mps_arena_t); @@ -647,17 +654,7 @@ extern mps_res_t mps_reserve_with_reservoir_permit(mps_addr_t *, MPS_END -#define MPS_RESERVE_WITH_RESERVOIR_PERMIT_BLOCK(_res_v, _p_v, _mps_ap, _size) \ - MPS_BEGIN \ - char *_alloc = (char *)(_mps_ap)->alloc; \ - char *_next = _alloc + (_size); \ - if(_next > _alloc && _next <= (char *)(_mps_ap)->limit) { \ - (_mps_ap)->alloc = (mps_addr_t)_next; \ - (_p_v) = (_mps_ap)->init; \ - (_res_v) = MPS_RES_OK; \ - } else \ - (_res_v) = mps_ap_fill_with_reservoir_permit(&(_p_v), _mps_ap, _size); \ - MPS_END +#define MPS_RESERVE_WITH_RESERVOIR_PERMIT_BLOCK MPS_RESERVE_BLOCK /* Commit Macros */ diff --git a/mps/code/mps.xcodeproj/project.pbxproj b/mps/code/mps.xcodeproj/project.pbxproj index e36b800455b..0980991ffa2 100644 --- a/mps/code/mps.xcodeproj/project.pbxproj +++ b/mps/code/mps.xcodeproj/project.pbxproj @@ -93,6 +93,7 @@ 2291A5E8175CB20E001D4920 /* PBXTargetDependency */, 3114A5CC156E932C001E0AA3 /* PBXTargetDependency */, 3114A5EA156E93C4001E0AA3 /* PBXTargetDependency */, + 22EA3F4820D2B23F0065F5B6 /* PBXTargetDependency */, 224CC79D175E187C002FF81B /* PBXTargetDependency */, 22B2BC3F18B643B700C33E63 /* PBXTargetDependency */, 3114A65B156E95B4001E0AA3 /* PBXTargetDependency */, @@ -112,6 +113,7 @@ 22B2BC3918B643AD00C33E63 /* PBXTargetDependency */, 22B2BC3B18B643B000C33E63 /* PBXTargetDependency */, 3104B04A156D3AE4000A585A /* PBXTargetDependency */, + 229E228819EAB10D00E21417 /* PBXTargetDependency */, 31D6009D156D404B00337B26 /* PBXTargetDependency */, 314CB6EB1C6D272A0073CA42 /* PBXTargetDependency */, 3114A62E156E94AA001E0AA3 /* PBXTargetDependency */, @@ -135,6 +137,9 @@ 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 */; }; + 223E795D19EAB00B00DC26A6 /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; }; + 223E795F19EAB00B00DC26A6 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; }; + 223E796719EAB05C00DC26A6 /* sncss.c in Sources */ = {isa = PBXBuildFile; fileRef = 223E796619EAB04100DC26A6 /* sncss.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 */; }; @@ -168,6 +173,9 @@ 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 */; }; + 22EA3F3D20D2B0D90065F5B6 /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; }; + 22EA3F3F20D2B0D90065F5B6 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; }; + 22EA3F4620D2B0FD0065F5B6 /* forktest.c in Sources */ = {isa = PBXBuildFile; fileRef = 22EA3F3720D2B0730065F5B6 /* forktest.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 */; }; @@ -268,6 +276,8 @@ 3114A6D1156E9829001E0AA3 /* eventcnv.c in Sources */ = {isa = PBXBuildFile; fileRef = 3114A6D0156E9829001E0AA3 /* eventcnv.c */; }; 3114A6D7156E9923001E0AA3 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; }; 3114A6DD156E9A0F001E0AA3 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; }; + 311A44F81C8B1EBD00852E2B /* testthrix.c in Sources */ = {isa = PBXBuildFile; fileRef = 22561A9718F4263300372C66 /* testthrix.c */; }; + 311A44F91C8B1EC200852E2B /* testthrix.c in Sources */ = {isa = PBXBuildFile; fileRef = 22561A9718F4263300372C66 /* testthrix.c */; }; 3124CAC3156BE40100753214 /* awlut.c in Sources */ = {isa = PBXBuildFile; fileRef = 3124CAC2156BE40100753214 /* awlut.c */; }; 3124CAC4156BE40D00753214 /* libmps.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 31EEABFB156AAF9D00714D05 /* libmps.a */; }; 3124CAC5156BE41700753214 /* testlib.c in Sources */ = {isa = PBXBuildFile; fileRef = 31EEAC9E156AB73400714D05 /* testlib.c */; }; @@ -394,6 +404,13 @@ remoteGlobalIDString = 2231BB5A18CA97DC002D6322; remoteInfo = locusss; }; + 223E795A19EAB00B00DC26A6 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 31EEABFA156AAF9D00714D05; + remoteInfo = mps; + }; 224CC78E175E1821002FF81B /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; @@ -464,6 +481,13 @@ remoteGlobalIDString = 2291A5C1175CAFCA001D4920; remoteInfo = expt825; }; + 229E228719EAB10D00E21417 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 223E795819EAB00B00DC26A6; + remoteInfo = sncss; + }; 22B2BC3818B643AD00C33E63 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; @@ -513,6 +537,20 @@ remoteGlobalIDString = 3104AFF1156D37A0000A585A; remoteInfo = all; }; + 22EA3F3A20D2B0D90065F5B6 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 31EEABFA156AAF9D00714D05; + remoteInfo = mps; + }; + 22EA3F4720D2B23F0065F5B6 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 22EA3F3820D2B0D90065F5B6; + remoteInfo = forktest; + }; 22F846B218F437B900982BA7 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 31EEABDA156AAE9E00714D05 /* Project object */; @@ -1017,6 +1055,15 @@ ); runOnlyForDeploymentPostprocessing = 1; }; + 223E796019EAB00B00DC26A6 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; 224CC794175E1821002FF81B /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -1071,6 +1118,15 @@ ); runOnlyForDeploymentPostprocessing = 1; }; + 22EA3F4020D2B0D90065F5B6 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; 22F846B818F437B900982BA7 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -1425,12 +1481,18 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 2213454C1DB0386600E14202 /* prmc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = prmc.h; sourceTree = ""; }; + 2213454D1DB038D400E14202 /* prmcxc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = prmcxc.c; sourceTree = ""; }; 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 = ""; }; + 2239BB4C20EE2E34007AC917 /* rangetree.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = rangetree.c; sourceTree = ""; }; + 2239BB4D20EE2E4D007AC917 /* rangetree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rangetree.h; sourceTree = ""; }; + 223E796519EAB00B00DC26A6 /* sncss */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = sncss; sourceTree = BUILT_PRODUCTS_DIR; }; + 223E796619EAB04100DC26A6 /* sncss.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sncss.c; 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 = ""; }; @@ -1460,6 +1522,8 @@ 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 = ""; }; + 22EA3F3720D2B0730065F5B6 /* forktest.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = forktest.c; sourceTree = ""; }; + 22EA3F4520D2B0D90065F5B6 /* forktest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = forktest; sourceTree = BUILT_PRODUCTS_DIR; }; 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; }; @@ -1483,7 +1547,6 @@ 2D07B97B163705E400DB751B /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; }; 2D604B9C16514B1A003AAF46 /* mpseventtxt */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mpseventtxt; sourceTree = BUILT_PRODUCTS_DIR; }; 2D604BA416514C4F003AAF46 /* eventtxt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = eventtxt.c; sourceTree = ""; }; - 3104AFA5156D27E7000A585A /* ssixi6.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ssixi6.c; sourceTree = ""; }; 3104AFB3156D357B000A585A /* apss */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = apss; sourceTree = BUILT_PRODUCTS_DIR; }; 3104AFBE156D3591000A585A /* apss.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = apss.c; sourceTree = ""; }; 3104AFC8156D35E2000A585A /* sacss */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = sacss; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1496,6 +1559,49 @@ 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 = ""; }; + 310EA5E21C889F4C004FE6B7 /* abq.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = abq.txt; path = ../design/abq.txt; sourceTree = ""; }; + 310EA5E41C889F4C004FE6B7 /* an.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = an.txt; path = ../design/an.txt; sourceTree = ""; }; + 310EA5E51C889F4C004FE6B7 /* arena.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = arena.txt; path = ../design/arena.txt; sourceTree = ""; }; + 310EA5E71C889F4C004FE6B7 /* bootstrap.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = bootstrap.txt; path = ../design/bootstrap.txt; sourceTree = ""; }; + 310EA5E81C889F4C004FE6B7 /* bt.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = bt.txt; path = ../design/bt.txt; sourceTree = ""; }; + 310EA5EA1C889F4C004FE6B7 /* cbs.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = cbs.txt; path = ../design/cbs.txt; sourceTree = ""; }; + 310EA5EC1C889F4C004FE6B7 /* class-interface.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = "class-interface.txt"; path = "../design/class-interface.txt"; sourceTree = ""; }; + 310EA5EE1C889F4C004FE6B7 /* config.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = config.txt; path = ../design/config.txt; sourceTree = ""; }; + 310EA5F01C889F4C004FE6B7 /* diag.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = diag.txt; path = ../design/diag.txt; sourceTree = ""; }; + 310EA5F11C889F4C004FE6B7 /* exec-env.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = "exec-env.txt"; path = "../design/exec-env.txt"; sourceTree = ""; }; + 310EA5F31C889F4C004FE6B7 /* finalize.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = finalize.txt; path = ../design/finalize.txt; sourceTree = ""; }; + 310EA5F51C889F4C004FE6B7 /* freelist.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = freelist.txt; path = ../design/freelist.txt; sourceTree = ""; }; + 310EA5F71C889F4C004FE6B7 /* guide.impl.c.format.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = guide.impl.c.format.txt; path = ../design/guide.impl.c.format.txt; sourceTree = ""; }; + 310EA5F81C889F4C004FE6B7 /* guide.impl.c.naming.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = guide.impl.c.naming.txt; path = ../design/guide.impl.c.naming.txt; sourceTree = ""; }; + 310EA5F91C889F4C004FE6B7 /* guide.review.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = guide.review.txt; path = ../design/guide.review.txt; sourceTree = ""; }; + 310EA5FB1C889F4C004FE6B7 /* interface-c.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = "interface-c.txt"; path = "../design/interface-c.txt"; sourceTree = ""; }; + 310EA5FD1C889F4C004FE6B7 /* keyword-arguments.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = "keyword-arguments.txt"; path = "../design/keyword-arguments.txt"; sourceTree = ""; }; + 310EA5FF1C889F4C004FE6B7 /* lib.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = lib.txt; path = ../design/lib.txt; sourceTree = ""; }; + 310EA6011C889F4C004FE6B7 /* locus.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = locus.txt; path = ../design/locus.txt; sourceTree = ""; }; + 310EA6031C889F4C004FE6B7 /* message.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = message.txt; path = ../design/message.txt; sourceTree = ""; }; + 310EA6071C889F4C004FE6B7 /* nailboard.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = nailboard.txt; path = ../design/nailboard.txt; sourceTree = ""; }; + 310EA6091C889F4C004FE6B7 /* pool.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = pool.txt; path = ../design/pool.txt; sourceTree = ""; }; + 310EA60B1C889F4C004FE6B7 /* poolams.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = poolams.txt; path = ../design/poolams.txt; sourceTree = ""; }; + 310EA60D1C889F4C004FE6B7 /* poollo.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = poollo.txt; path = ../design/poollo.txt; sourceTree = ""; }; + 310EA60F1C889F4C004FE6B7 /* poolmrg.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = poolmrg.txt; path = ../design/poolmrg.txt; sourceTree = ""; }; + 310EA6111C889F4C004FE6B7 /* poolmvff.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = poolmvff.txt; path = ../design/poolmvff.txt; sourceTree = ""; }; + 310EA6131C889F4C004FE6B7 /* prmc.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = prmc.txt; path = ../design/prmc.txt; sourceTree = ""; }; + 310EA6141C889F4C004FE6B7 /* prot.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = prot.txt; path = ../design/prot.txt; sourceTree = ""; }; + 310EA6161C889F4C004FE6B7 /* protocol.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = protocol.txt; path = ../design/protocol.txt; sourceTree = ""; }; + 310EA6181C889F4C004FE6B7 /* pthreadext.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = pthreadext.txt; path = ../design/pthreadext.txt; sourceTree = ""; }; + 310EA61A1C889F4C004FE6B7 /* reservoir.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = reservoir.txt; path = ../design/reservoir.txt; sourceTree = ""; }; + 310EA61C1C889F4C004FE6B7 /* root.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = root.txt; path = ../design/root.txt; sourceTree = ""; }; + 310EA61E1C889F4C004FE6B7 /* seg.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = seg.txt; path = ../design/seg.txt; sourceTree = ""; }; + 310EA6201C889F4C004FE6B7 /* sig.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = sig.txt; path = ../design/sig.txt; sourceTree = ""; }; + 310EA6211C889F4C004FE6B7 /* sp.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = sp.txt; path = ../design/sp.txt; sourceTree = ""; }; + 310EA6231C889F4C004FE6B7 /* stack-scan.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = "stack-scan.txt"; path = "../design/stack-scan.txt"; sourceTree = ""; }; + 310EA6261C889F4C004FE6B7 /* telemetry.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = telemetry.txt; path = ../design/telemetry.txt; sourceTree = ""; }; + 310EA6281C889F4C004FE6B7 /* testthr.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = testthr.txt; path = ../design/testthr.txt; sourceTree = ""; }; + 310EA6291C889F4C004FE6B7 /* thread-manager.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = "thread-manager.txt"; path = "../design/thread-manager.txt"; sourceTree = ""; }; + 310EA62B1C889F4C004FE6B7 /* trace.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = trace.txt; path = ../design/trace.txt; sourceTree = ""; }; + 310EA62D1C889F4C004FE6B7 /* version-library.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = "version-library.txt"; path = "../design/version-library.txt"; sourceTree = ""; }; + 310EA62F1C889F4C004FE6B7 /* vm.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = vm.txt; path = ../design/vm.txt; sourceTree = ""; }; + 310EA6311C889F4C004FE6B7 /* vmso.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = vmso.txt; path = ../design/vmso.txt; 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 = ""; }; 31108A391C6B90D600E728EA /* tagtest.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = tagtest.c; sourceTree = ""; }; @@ -1538,7 +1644,7 @@ 31160D971899540D0071EB17 /* buffer.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = buffer.txt; path = ../design/buffer.txt; sourceTree = ""; }; 31160D981899540D0071EB17 /* cbs.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = cbs.txt; path = ../design/cbs.txt; sourceTree = ""; }; 31160D991899540D0071EB17 /* check.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = check.txt; path = ../design/check.txt; sourceTree = ""; }; - 31160D9A1899540D0071EB17 /* class-interface.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = "class-interface.txt"; path = "../design/class-interface.txt"; sourceTree = ""; }; + 31160D9A1899540D0071EB17 /* pool.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = pool.txt; path = ../design/pool.txt; sourceTree = ""; }; 31160D9B1899540D0071EB17 /* collection.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = collection.txt; path = ../design/collection.txt; sourceTree = ""; }; 31160D9C1899540D0071EB17 /* config.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = config.txt; path = ../design/config.txt; sourceTree = ""; }; 31160D9D1899540D0071EB17 /* critical-path.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = "critical-path.txt"; path = "../design/critical-path.txt"; sourceTree = ""; }; @@ -1558,14 +1664,12 @@ 31160DAB1899540D0071EB17 /* message-gc.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = "message-gc.txt"; path = "../design/message-gc.txt"; sourceTree = ""; }; 31160DAC1899540D0071EB17 /* message.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = message.txt; path = ../design/message.txt; sourceTree = ""; }; 31160DAD1899540D0071EB17 /* object-debug.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = "object-debug.txt"; path = "../design/object-debug.txt"; sourceTree = ""; }; - 31160DAE1899540D0071EB17 /* pool.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = pool.txt; path = ../design/pool.txt; sourceTree = ""; }; 31160DAF1899540D0071EB17 /* poolamc.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = poolamc.txt; path = ../design/poolamc.txt; sourceTree = ""; }; 31160DB01899540D0071EB17 /* poolams.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = poolams.txt; path = ../design/poolams.txt; sourceTree = ""; }; 31160DB11899540D0071EB17 /* poolawl.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = poolawl.txt; path = ../design/poolawl.txt; sourceTree = ""; }; 31160DB21899540D0071EB17 /* poollo.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = poollo.txt; path = ../design/poollo.txt; sourceTree = ""; }; 31160DB31899540D0071EB17 /* poolmfs.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = poolmfs.txt; path = ../design/poolmfs.txt; sourceTree = ""; }; 31160DB41899540D0071EB17 /* poolmrg.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = poolmrg.txt; path = ../design/poolmrg.txt; sourceTree = ""; }; - 31160DB51899540D0071EB17 /* poolmv.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = poolmv.txt; path = ../design/poolmv.txt; sourceTree = ""; }; 31160DB61899540D0071EB17 /* poolmvff.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = poolmvff.txt; path = ../design/poolmvff.txt; sourceTree = ""; }; 31160DB71899540D0071EB17 /* poolmvt.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = poolmvt.txt; path = ../design/poolmvt.txt; sourceTree = ""; }; 31160DB81899540D0071EB17 /* prot.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = prot.txt; path = ../design/prot.txt; sourceTree = ""; }; @@ -1574,7 +1678,6 @@ 31160DBC1899540D0071EB17 /* protsu.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = protsu.txt; path = ../design/protsu.txt; sourceTree = ""; }; 31160DBD1899540D0071EB17 /* pthreadext.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = pthreadext.txt; path = ../design/pthreadext.txt; sourceTree = ""; }; 31160DBE1899540D0071EB17 /* range.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = range.txt; path = ../design/range.txt; sourceTree = ""; }; - 31160DBF1899540D0071EB17 /* reservoir.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = reservoir.txt; path = ../design/reservoir.txt; sourceTree = ""; }; 31160DC01899540D0071EB17 /* ring.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = ring.txt; path = ../design/ring.txt; sourceTree = ""; }; 31160DC11899540D0071EB17 /* root.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = root.txt; path = ../design/root.txt; sourceTree = ""; }; 31160DC21899540D0071EB17 /* scan.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = scan.txt; path = ../design/scan.txt; sourceTree = ""; }; @@ -1582,7 +1685,6 @@ 31160DC41899540D0071EB17 /* shield.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = shield.txt; path = ../design/shield.txt; sourceTree = ""; }; 31160DC51899540D0071EB17 /* sig.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = sig.txt; path = ../design/sig.txt; sourceTree = ""; }; 31160DC61899540D0071EB17 /* splay.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = splay.txt; path = ../design/splay.txt; sourceTree = ""; }; - 31160DC71899540D0071EB17 /* sso1al.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = sso1al.txt; path = ../design/sso1al.txt; sourceTree = ""; }; 31160DC81899540D0071EB17 /* strategy.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = strategy.txt; path = ../design/strategy.txt; sourceTree = ""; }; 31160DC91899540D0071EB17 /* telemetry.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = telemetry.txt; path = ../design/telemetry.txt; sourceTree = ""; }; 31160DCA1899540D0071EB17 /* tests.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = tests.txt; path = ../design/tests.txt; sourceTree = ""; }; @@ -1597,8 +1699,8 @@ 31160DD41899540D0071EB17 /* vmso.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = vmso.txt; path = ../design/vmso.txt; sourceTree = ""; }; 31160DD51899540D0071EB17 /* writef.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = writef.txt; path = ../design/writef.txt; sourceTree = ""; }; 31172ABA17750F9D009488E5 /* thxc.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = thxc.c; sourceTree = ""; }; - 31172ABB177512F6009488E5 /* prmci3xc.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = prmci3xc.c; sourceTree = ""; }; - 31172ABC1775131C009488E5 /* prmci6xc.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = prmci6xc.c; sourceTree = ""; }; + 31172ABB177512F6009488E5 /* prmcxci3.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = prmcxci3.c; sourceTree = ""; }; + 31172ABC1775131C009488E5 /* prmcxci6.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = prmcxci6.c; sourceTree = ""; }; 31172ABE1775164F009488E5 /* prmcxc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = prmcxc.h; sourceTree = ""; }; 31172AC017752253009488E5 /* protxc.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = protxc.c; sourceTree = ""; }; 311F2F5017398AD500C15B6A /* boot.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = boot.h; sourceTree = ""; }; @@ -1611,7 +1713,6 @@ 311F2F5817398AE900C15B6A /* event.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = event.h; sourceTree = ""; }; 311F2F5917398AE900C15B6A /* eventcom.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = eventcom.h; sourceTree = ""; }; 311F2F5A17398AE900C15B6A /* eventdef.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = eventdef.h; sourceTree = ""; }; - 311F2F5C17398AE900C15B6A /* eventrep.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = eventrep.h; sourceTree = ""; }; 311F2F5E17398B0E00C15B6A /* lock.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = lock.h; sourceTree = ""; }; 311F2F5F17398B0E00C15B6A /* meter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = meter.h; sourceTree = ""; }; 311F2F6017398B0E00C15B6A /* misc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = misc.h; sourceTree = ""; }; @@ -1627,20 +1728,14 @@ 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 = ""; }; - 311F2F6F17398B6300C15B6A /* prmcix.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = prmcix.h; sourceTree = ""; }; - 311F2F7017398B6300C15B6A /* prmcw3.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = prmcw3.h; sourceTree = ""; }; 311F2F7117398B7100C15B6A /* protocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = protocol.h; sourceTree = ""; }; - 311F2F7217398B7100C15B6A /* pthrdext.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = pthrdext.h; sourceTree = ""; }; 311F2F7317398B7100C15B6A /* ring.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ring.h; sourceTree = ""; }; 311F2F7417398B7100C15B6A /* sac.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = sac.h; sourceTree = ""; }; - 311F2F7517398B8E00C15B6A /* sc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = sc.h; sourceTree = ""; }; 311F2F7617398B8E00C15B6A /* splay.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = splay.h; sourceTree = ""; }; 311F2F7717398B8E00C15B6A /* ss.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ss.h; sourceTree = ""; }; 311F2F7817398B8E00C15B6A /* th.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = th.h; sourceTree = ""; }; - 311F2F7917398B8E00C15B6A /* thw3.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = thw3.h; sourceTree = ""; }; 311F2F7A17398B8E00C15B6A /* tract.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = tract.h; sourceTree = ""; }; - 311F2F7B17398E7600C15B6A /* poolmv.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = poolmv.h; sourceTree = ""; }; - 311F2F7C17398E9A00C15B6A /* mpscmv.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = mpscmv.h; sourceTree = ""; }; + 311F2F7B17398E7600C15B6A /* poolmvff.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = poolmvff.h; sourceTree = ""; }; 3124CAB8156BE3EC00753214 /* awlut */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = awlut; sourceTree = BUILT_PRODUCTS_DIR; }; 3124CAC2156BE40100753214 /* awlut.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = awlut.c; sourceTree = ""; }; 3124CAC6156BE48D00753214 /* fmtdy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fmtdy.c; sourceTree = ""; }; @@ -1654,63 +1749,28 @@ 3124CAEB156BE7F300753214 /* amcss */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = amcss; sourceTree = BUILT_PRODUCTS_DIR; }; 3124CAF5156BE81100753214 /* amcss.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = amcss.c; sourceTree = ""; }; 314562191C72ABFA00D7A514 /* scan.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = scan.c; sourceTree = ""; }; - 315B7AFC17834FDB00B097C4 /* proti3.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = proti3.c; sourceTree = ""; }; - 315B7AFD17834FDB00B097C4 /* proti6.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = proti6.c; sourceTree = ""; }; + 315B7AFC17834FDB00B097C4 /* prmci3.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = prmci3.c; sourceTree = ""; }; + 315B7AFD17834FDB00B097C4 /* prmci6.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = prmci6.c; sourceTree = ""; }; 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 = ""; }; 31942A671C8EC3FC001AAF32 /* locus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = locus.h; sourceTree = ""; }; - 31942A681C8EC445001AAF32 /* abq.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = abq.txt; path = ../design/abq.txt; sourceTree = ""; }; 31942A6A1C8EC445001AAF32 /* an.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = an.txt; path = ../design/an.txt; sourceTree = ""; }; - 31942A6B1C8EC445001AAF32 /* arena.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = arena.txt; path = ../design/arena.txt; sourceTree = ""; }; 31942A6D1C8EC445001AAF32 /* boot.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = boot.txt; path = ../design/boot.txt; sourceTree = ""; }; 31942A6E1C8EC445001AAF32 /* bootstrap.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = bootstrap.txt; path = ../design/bootstrap.txt; sourceTree = ""; }; - 31942A6F1C8EC445001AAF32 /* bt.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = bt.txt; path = ../design/bt.txt; sourceTree = ""; }; - 31942A711C8EC445001AAF32 /* cbs.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = cbs.txt; path = ../design/cbs.txt; sourceTree = ""; }; - 31942A731C8EC445001AAF32 /* class-interface.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = "class-interface.txt"; path = "../design/class-interface.txt"; sourceTree = ""; }; 31942A741C8EC445001AAF32 /* clock.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = clock.txt; path = ../design/clock.txt; sourceTree = ""; }; - 31942A761C8EC445001AAF32 /* config.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = config.txt; path = ../design/config.txt; sourceTree = ""; }; - 31942A781C8EC445001AAF32 /* diag.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = diag.txt; path = ../design/diag.txt; sourceTree = ""; }; 31942A791C8EC445001AAF32 /* exec-env.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = "exec-env.txt"; path = "../design/exec-env.txt"; sourceTree = ""; }; - 31942A7B1C8EC445001AAF32 /* finalize.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = finalize.txt; path = ../design/finalize.txt; sourceTree = ""; }; - 31942A7D1C8EC445001AAF32 /* freelist.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = freelist.txt; path = ../design/freelist.txt; sourceTree = ""; }; - 31942A7F1C8EC445001AAF32 /* guide.impl.c.format.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = guide.impl.c.format.txt; path = ../design/guide.impl.c.format.txt; sourceTree = ""; }; 31942A801C8EC445001AAF32 /* guide.impl.c.naming.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = guide.impl.c.naming.txt; path = ../design/guide.impl.c.naming.txt; sourceTree = ""; }; 31942A811C8EC445001AAF32 /* guide.review.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = guide.review.txt; path = ../design/guide.review.txt; sourceTree = ""; }; - 31942A831C8EC445001AAF32 /* interface-c.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = "interface-c.txt"; path = "../design/interface-c.txt"; sourceTree = ""; }; - 31942A851C8EC445001AAF32 /* keyword-arguments.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = "keyword-arguments.txt"; path = "../design/keyword-arguments.txt"; sourceTree = ""; }; - 31942A871C8EC445001AAF32 /* lib.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = lib.txt; path = ../design/lib.txt; sourceTree = ""; }; - 31942A891C8EC445001AAF32 /* locus.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = locus.txt; path = ../design/locus.txt; sourceTree = ""; }; - 31942A8B1C8EC446001AAF32 /* message.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = message.txt; path = ../design/message.txt; sourceTree = ""; }; 31942A8C1C8EC446001AAF32 /* nailboard-1.svg */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = "nailboard-1.svg"; path = "../design/nailboard-1.svg"; sourceTree = ""; }; 31942A8D1C8EC446001AAF32 /* nailboard-2.svg */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = "nailboard-2.svg"; path = "../design/nailboard-2.svg"; sourceTree = ""; }; 31942A8E1C8EC446001AAF32 /* nailboard-3.svg */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = "nailboard-3.svg"; path = "../design/nailboard-3.svg"; sourceTree = ""; }; 31942A8F1C8EC446001AAF32 /* nailboard.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = nailboard.txt; path = ../design/nailboard.txt; sourceTree = ""; }; - 31942A911C8EC446001AAF32 /* pool.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = pool.txt; path = ../design/pool.txt; sourceTree = ""; }; - 31942A931C8EC446001AAF32 /* poolams.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = poolams.txt; path = ../design/poolams.txt; sourceTree = ""; }; - 31942A951C8EC446001AAF32 /* poollo.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = poollo.txt; path = ../design/poollo.txt; sourceTree = ""; }; - 31942A971C8EC446001AAF32 /* poolmrg.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = poolmrg.txt; path = ../design/poolmrg.txt; sourceTree = ""; }; - 31942A991C8EC446001AAF32 /* poolmvff.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = poolmvff.txt; path = ../design/poolmvff.txt; sourceTree = ""; }; 31942A9B1C8EC446001AAF32 /* prmc.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = prmc.txt; path = ../design/prmc.txt; sourceTree = ""; }; - 31942A9C1C8EC446001AAF32 /* prot.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = prot.txt; path = ../design/prot.txt; sourceTree = ""; }; - 31942A9E1C8EC446001AAF32 /* protocol.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = protocol.txt; path = ../design/protocol.txt; sourceTree = ""; }; - 31942AA01C8EC446001AAF32 /* pthreadext.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = pthreadext.txt; path = ../design/pthreadext.txt; sourceTree = ""; }; - 31942AA21C8EC446001AAF32 /* reservoir.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = reservoir.txt; path = ../design/reservoir.txt; sourceTree = ""; }; - 31942AA41C8EC446001AAF32 /* root.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = root.txt; path = ../design/root.txt; sourceTree = ""; }; - 31942AA61C8EC446001AAF32 /* seg.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = seg.txt; path = ../design/seg.txt; sourceTree = ""; }; - 31942AA81C8EC446001AAF32 /* sig.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = sig.txt; path = ../design/sig.txt; sourceTree = ""; }; 31942AA91C8EC446001AAF32 /* sp.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = sp.txt; path = ../design/sp.txt; sourceTree = ""; }; - 31942AAB1C8EC446001AAF32 /* ss.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = ss.txt; path = ../design/ss.txt; sourceTree = ""; }; - 31942AAC1C8EC446001AAF32 /* sso1al.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = sso1al.txt; path = ../design/sso1al.txt; sourceTree = ""; }; - 31942AAE1C8EC446001AAF32 /* telemetry.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = telemetry.txt; path = ../design/telemetry.txt; sourceTree = ""; }; + 31942AAB1C8EC446001AAF32 /* stack-scan.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = stack-scan.txt; path = ../design/stack-scan.txt; sourceTree = ""; }; 31942AB01C8EC446001AAF32 /* testthr.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = testthr.txt; path = ../design/testthr.txt; sourceTree = ""; }; - 31942AB11C8EC446001AAF32 /* thread-manager.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = "thread-manager.txt"; path = "../design/thread-manager.txt"; sourceTree = ""; }; - 31942AB31C8EC446001AAF32 /* trace.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = trace.txt; path = ../design/trace.txt; sourceTree = ""; }; - 31942AB51C8EC446001AAF32 /* version-library.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = "version-library.txt"; path = "../design/version-library.txt"; sourceTree = ""; }; - 31942AB71C8EC446001AAF32 /* vm.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = vm.txt; path = ../design/vm.txt; sourceTree = ""; }; - 31942AB91C8EC446001AAF32 /* vmso.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = vmso.txt; path = ../design/vmso.txt; 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 = ""; }; 31CD33BB173A9F1500524741 /* mpscams.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = mpscams.h; sourceTree = ""; }; 31CD33BC173A9F1500524741 /* poolams.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = poolams.c; sourceTree = ""; }; @@ -1740,7 +1800,6 @@ 31EEAC09156AB27B00714D05 /* pool.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pool.c; sourceTree = ""; }; 31EEAC0A156AB27B00714D05 /* poolabs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = poolabs.c; sourceTree = ""; }; 31EEAC0B156AB27B00714D05 /* protocol.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = protocol.c; sourceTree = ""; }; - 31EEAC0C156AB27B00714D05 /* reserv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = reserv.c; sourceTree = ""; }; 31EEAC0D156AB27B00714D05 /* tract.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tract.c; sourceTree = ""; }; 31EEAC0E156AB27B00714D05 /* walk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = walk.c; sourceTree = ""; }; 31EEAC19156AB2B200714D05 /* buffer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = buffer.c; sourceTree = ""; }; @@ -1758,7 +1817,6 @@ 31EEAC2C156AB2F200714D05 /* message.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = message.c; sourceTree = ""; }; 31EEAC2D156AB2F200714D05 /* poolmfs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = poolmfs.c; sourceTree = ""; }; 31EEAC2E156AB2F200714D05 /* poolmrg.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = poolmrg.c; sourceTree = ""; }; - 31EEAC2F156AB2F200714D05 /* poolmv.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; lineEnding = 0; path = poolmv.c; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.c; }; 31EEAC30156AB2F200714D05 /* ring.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ring.c; sourceTree = ""; }; 31EEAC31156AB2F200714D05 /* sac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sac.c; sourceTree = ""; }; 31EEAC32156AB2F200714D05 /* shield.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = shield.c; sourceTree = ""; }; @@ -1807,6 +1865,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 223E795E19EAB00B00DC26A6 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 223E795F19EAB00B00DC26A6 /* libmps.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 224CC792175E1821002FF81B /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -1854,6 +1920,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 22EA3F3E20D2B0D90065F5B6 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 22EA3F3F20D2B0D90065F5B6 /* libmps.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 22F846B618F437B900982BA7 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -2210,108 +2284,31 @@ 31160D90189953D50071EB17 /* Design */ = { isa = PBXGroup; children = ( - 31942A681C8EC445001AAF32 /* abq.txt */, + 31160D921899540D0071EB17 /* abq.txt */, 31160D931899540D0071EB17 /* alloc-frame.txt */, 31942A6A1C8EC445001AAF32 /* an.txt */, - 31942A6B1C8EC445001AAF32 /* arena.txt */, + 31160D941899540D0071EB17 /* arena.txt */, 31160D951899540D0071EB17 /* arenavm.txt */, 31942A6D1C8EC445001AAF32 /* boot.txt */, 31942A6E1C8EC445001AAF32 /* bootstrap.txt */, - 31942A6F1C8EC445001AAF32 /* bt.txt */, - 31160D971899540D0071EB17 /* buffer.txt */, - 31942A711C8EC445001AAF32 /* cbs.txt */, - 31160D991899540D0071EB17 /* check.txt */, - 31942A731C8EC445001AAF32 /* class-interface.txt */, - 31942A741C8EC445001AAF32 /* clock.txt */, - 31160D9B1899540D0071EB17 /* collection.txt */, - 31942A761C8EC445001AAF32 /* config.txt */, - 31160D9D1899540D0071EB17 /* critical-path.txt */, - 31942A781C8EC445001AAF32 /* diag.txt */, - 31942A791C8EC445001AAF32 /* exec-env.txt */, - 22DD93E118ED815F00240DD2 /* failover.txt */, - 31942A7B1C8EC445001AAF32 /* finalize.txt */, - 31160DA01899540D0071EB17 /* fix.txt */, - 31942A7D1C8EC445001AAF32 /* freelist.txt */, - 31160DA21899540D0071EB17 /* guide.hex.trans.txt */, - 31942A7F1C8EC445001AAF32 /* guide.impl.c.format.txt */, - 31942A801C8EC445001AAF32 /* guide.impl.c.naming.txt */, - 31942A811C8EC445001AAF32 /* guide.review.txt */, - 31160DA41899540D0071EB17 /* index.txt */, - 31942A831C8EC445001AAF32 /* interface-c.txt */, - 31160DA61899540D0071EB17 /* io.txt */, - 31942A851C8EC445001AAF32 /* keyword-arguments.txt */, - 22DD93E218ED815F00240DD2 /* land.txt */, - 31942A871C8EC445001AAF32 /* lib.txt */, - 31160DA91899540D0071EB17 /* lock.txt */, - 31942A891C8EC445001AAF32 /* locus.txt */, - 31160DAB1899540D0071EB17 /* message-gc.txt */, - 31942A8B1C8EC446001AAF32 /* message.txt */, - 31942A8C1C8EC446001AAF32 /* nailboard-1.svg */, - 31942A8D1C8EC446001AAF32 /* nailboard-2.svg */, - 31942A8E1C8EC446001AAF32 /* nailboard-3.svg */, - 31942A8F1C8EC446001AAF32 /* nailboard.txt */, - 31160DAD1899540D0071EB17 /* object-debug.txt */, - 31942A911C8EC446001AAF32 /* pool.txt */, - 31160DAF1899540D0071EB17 /* poolamc.txt */, - 31942A931C8EC446001AAF32 /* poolams.txt */, - 31160DB11899540D0071EB17 /* poolawl.txt */, - 31942A951C8EC446001AAF32 /* poollo.txt */, - 31160DB31899540D0071EB17 /* poolmfs.txt */, - 31942A971C8EC446001AAF32 /* poolmrg.txt */, - 31160DB51899540D0071EB17 /* poolmv.txt */, - 31942A991C8EC446001AAF32 /* poolmvff.txt */, - 31160DB71899540D0071EB17 /* poolmvt.txt */, - 31942A9B1C8EC446001AAF32 /* prmc.txt */, - 31942A9C1C8EC446001AAF32 /* prot.txt */, - 31160DBA1899540D0071EB17 /* protli.txt */, - 31942A9E1C8EC446001AAF32 /* protocol.txt */, - 31160DBC1899540D0071EB17 /* protsu.txt */, - 31942AA01C8EC446001AAF32 /* pthreadext.txt */, - 31160DBE1899540D0071EB17 /* range.txt */, - 31942AA21C8EC446001AAF32 /* reservoir.txt */, - 31160DC01899540D0071EB17 /* ring.txt */, - 31942AA41C8EC446001AAF32 /* root.txt */, - 31160DC21899540D0071EB17 /* scan.txt */, - 31942AA61C8EC446001AAF32 /* seg.txt */, - 31160DC41899540D0071EB17 /* shield.txt */, - 31942AA81C8EC446001AAF32 /* sig.txt */, - 31942AA91C8EC446001AAF32 /* sp.txt */, - 31160DC61899540D0071EB17 /* splay.txt */, - 31942AAB1C8EC446001AAF32 /* ss.txt */, - 31942AAC1C8EC446001AAF32 /* sso1al.txt */, - 31160DC81899540D0071EB17 /* strategy.txt */, - 31942AAE1C8EC446001AAF32 /* telemetry.txt */, - 31160DCA1899540D0071EB17 /* tests.txt */, - 31942AB01C8EC446001AAF32 /* testthr.txt */, - 31942AB11C8EC446001AAF32 /* thread-manager.txt */, - 31160DCC1899540D0071EB17 /* thread-safety.txt */, - 31942AB31C8EC446001AAF32 /* trace.txt */, - 31160DCE1899540D0071EB17 /* type.txt */, - 31942AB51C8EC446001AAF32 /* version-library.txt */, - 31160DD01899540D0071EB17 /* version.txt */, - 31942AB71C8EC446001AAF32 /* vm.txt */, - 31160DD31899540D0071EB17 /* vmo1.txt */, - 31942AB91C8EC446001AAF32 /* vmso.txt */, - 31160D921899540D0071EB17 /* abq.txt */, - 31160DD51899540D0071EB17 /* writef.txt */, - 31160D931899540D0071EB17 /* alloc-frame.txt */, - 31160D941899540D0071EB17 /* arena.txt */, - 31160D951899540D0071EB17 /* arenavm.txt */, 31160D961899540D0071EB17 /* bt.txt */, 31160D971899540D0071EB17 /* buffer.txt */, 31160D981899540D0071EB17 /* cbs.txt */, 31160D991899540D0071EB17 /* check.txt */, - 31160D9A1899540D0071EB17 /* class-interface.txt */, + 31942A741C8EC445001AAF32 /* clock.txt */, 31160D9B1899540D0071EB17 /* collection.txt */, 31160D9C1899540D0071EB17 /* config.txt */, 31160D9D1899540D0071EB17 /* critical-path.txt */, 31160D9E1899540D0071EB17 /* diag.txt */, + 31942A791C8EC445001AAF32 /* exec-env.txt */, 22DD93E118ED815F00240DD2 /* failover.txt */, 31160D9F1899540D0071EB17 /* finalize.txt */, 31160DA01899540D0071EB17 /* fix.txt */, 31160DA11899540D0071EB17 /* freelist.txt */, 31160DA21899540D0071EB17 /* guide.hex.trans.txt */, 31160DA31899540D0071EB17 /* guide.impl.c.format.txt */, + 31942A801C8EC445001AAF32 /* guide.impl.c.naming.txt */, + 31942A811C8EC445001AAF32 /* guide.review.txt */, 31160DA41899540D0071EB17 /* index.txt */, 31160DA51899540D0071EB17 /* interface-c.txt */, 31160DA61899540D0071EB17 /* io.txt */, @@ -2322,35 +2319,41 @@ 31160DAA1899540D0071EB17 /* locus.txt */, 31160DAB1899540D0071EB17 /* message-gc.txt */, 31160DAC1899540D0071EB17 /* message.txt */, + 31942A8C1C8EC446001AAF32 /* nailboard-1.svg */, + 31942A8D1C8EC446001AAF32 /* nailboard-2.svg */, + 31942A8E1C8EC446001AAF32 /* nailboard-3.svg */, + 31942A8F1C8EC446001AAF32 /* nailboard.txt */, 31160DAD1899540D0071EB17 /* object-debug.txt */, - 31160DAE1899540D0071EB17 /* pool.txt */, + 31160D9A1899540D0071EB17 /* pool.txt */, 31160DAF1899540D0071EB17 /* poolamc.txt */, 31160DB01899540D0071EB17 /* poolams.txt */, 31160DB11899540D0071EB17 /* poolawl.txt */, 31160DB21899540D0071EB17 /* poollo.txt */, 31160DB31899540D0071EB17 /* poolmfs.txt */, 31160DB41899540D0071EB17 /* poolmrg.txt */, - 31160DB51899540D0071EB17 /* poolmv.txt */, 31160DB61899540D0071EB17 /* poolmvff.txt */, 31160DB71899540D0071EB17 /* poolmvt.txt */, + 31942A9B1C8EC446001AAF32 /* prmc.txt */, 31160DB81899540D0071EB17 /* prot.txt */, 31160DBA1899540D0071EB17 /* protli.txt */, 31160DBB1899540D0071EB17 /* protocol.txt */, 31160DBC1899540D0071EB17 /* protsu.txt */, 31160DBD1899540D0071EB17 /* pthreadext.txt */, 31160DBE1899540D0071EB17 /* range.txt */, - 31160DBF1899540D0071EB17 /* reservoir.txt */, 31160DC01899540D0071EB17 /* ring.txt */, 31160DC11899540D0071EB17 /* root.txt */, 31160DC21899540D0071EB17 /* scan.txt */, 31160DC31899540D0071EB17 /* seg.txt */, 31160DC41899540D0071EB17 /* shield.txt */, 31160DC51899540D0071EB17 /* sig.txt */, + 31942AA91C8EC446001AAF32 /* sp.txt */, 31160DC61899540D0071EB17 /* splay.txt */, + 31942AAB1C8EC446001AAF32 /* stack-scan.txt */, 31160DC71899540D0071EB17 /* sso1al.txt */, 31160DC81899540D0071EB17 /* strategy.txt */, 31160DC91899540D0071EB17 /* telemetry.txt */, 31160DCA1899540D0071EB17 /* tests.txt */, + 31942AB01C8EC446001AAF32 /* testthr.txt */, 31160DCB1899540D0071EB17 /* thread-manager.txt */, 31160DCC1899540D0071EB17 /* thread-safety.txt */, 31160DCD1899540D0071EB17 /* trace.txt */, @@ -2368,7 +2371,6 @@ 3124CAB3156BE1B700753214 /* Tests */ = { isa = PBXGroup; children = ( - 22F846AF18F4379C00982BA7 /* lockut.c */, 3114A63D156E94EA001E0AA3 /* abqtest.c */, 22FACED1188807FF000FDBC1 /* airtest.c */, 3124CAF5156BE81100753214 /* amcss.c */, @@ -2397,10 +2399,12 @@ 22FACED5188807FF000FDBC1 /* fmtno.h */, 22FACED6188807FF000FDBC1 /* fmtscheme.c */, 22FACED7188807FF000FDBC1 /* fmtscheme.h */, + 22EA3F3720D2B0730065F5B6 /* forktest.c */, 224CC79E175E3202002FF81B /* fotest.c */, 2291A5E9175CB4EC001D4920 /* landtest.c */, 2231BB6818CA9834002D6322 /* locbwcss.c */, 31D60036156D3E0200337B26 /* lockcov.c */, + 22F846AF18F4379C00982BA7 /* lockut.c */, 2231BB6918CA983C002D6322 /* locusss.c */, 3114A5A1156E9168001E0AA3 /* locv.c */, 3114A69F156E9725001E0AA3 /* messtest.c */, @@ -2412,6 +2416,7 @@ 3114A5B7156E92F0001E0AA3 /* qs.c */, 3104AFD6156D3602000A585A /* sacss.c */, 31D60006156D3C5F00337B26 /* segsmss.c */, + 223E796619EAB04100DC26A6 /* sncss.c */, 31D60098156D403C00337B26 /* steptest.c */, 31108A391C6B90D600E728EA /* tagtest.c */, 3114A628156E949A001E0AA3 /* teletest.c */, @@ -2512,6 +2517,8 @@ 22C2ACAF18BE400A006B3677 /* nailboardtest */, 22F846BD18F437B900982BA7 /* lockut */, 31108A471C6B90E900E728EA /* tagtest */, + 223E796519EAB00B00DC26A6 /* sncss */, + 22EA3F4520D2B0D90065F5B6 /* forktest */, ); name = Products; sourceTree = ""; @@ -2519,7 +2526,6 @@ 31EEABF4156AAF6500714D05 /* MPM Core */ = { isa = PBXGroup; children = ( - 31942A671C8EC3FC001AAF32 /* locus.h */, 3114A645156E9525001E0AA3 /* abq.c */, 2291A5EA175CB503001D4920 /* abq.h */, 31EEAC05156AB27B00714D05 /* arena.c */, @@ -2544,7 +2550,6 @@ 311F2F5817398AE900C15B6A /* event.h */, 311F2F5917398AE900C15B6A /* eventcom.h */, 311F2F5A17398AE900C15B6A /* eventdef.h */, - 311F2F5C17398AE900C15B6A /* eventrep.h */, 22C5C99A18EC6AEC004C63D4 /* failover.c */, 22C5C99B18EC6AEC004C63D4 /* failover.h */, 31EEAC1A156AB2B200714D05 /* format.c */, @@ -2555,6 +2560,7 @@ 31EEAC2B156AB2F200714D05 /* ld.c */, 311F2F5E17398B0E00C15B6A /* lock.h */, 31EEAC08156AB27B00714D05 /* locus.c */, + 31942A671C8EC3FC001AAF32 /* locus.h */, 31EEAC2C156AB2F200714D05 /* message.c */, 31EEAC42156AB32500714D05 /* meter.c */, 311F2F5F17398B0E00C15B6A /* meter.h */, @@ -2567,7 +2573,6 @@ 311F2F6517398B3B00C15B6A /* mpsacl.h */, 311F2F6617398B3B00C15B6A /* mpsavm.h */, 22FACEDB188808D5000FDBC1 /* mpscmfs.h */, - 311F2F7C17398E9A00C15B6A /* mpscmv.h */, 31EEABF5156AAF7C00714D05 /* mpsi.c */, 311F2F6717398B3B00C15B6A /* mpsio.h */, 311F2F6817398B3B00C15B6A /* mpslib.h */, @@ -2581,21 +2586,18 @@ 22FACEDC18880933000FDBC1 /* poolmfs.h */, 31EEAC2E156AB2F200714D05 /* poolmrg.c */, 22FACEDD18880933000FDBC1 /* poolmrg.h */, - 31EEAC2F156AB2F200714D05 /* poolmv.c */, - 311F2F7B17398E7600C15B6A /* poolmv.h */, + 31EEAC5F156AB44D00714D05 /* poolmvff.c */, + 311F2F7B17398E7600C15B6A /* poolmvff.h */, 22FACEDE18880933000FDBC1 /* pooln.c */, 22FACEDF18880933000FDBC1 /* pooln.h */, - 311F2F6D17398B6300C15B6A /* prmci3.h */, - 311F2F6E17398B6300C15B6A /* prmci6.h */, - 311F2F6F17398B6300C15B6A /* prmcix.h */, - 311F2F7017398B6300C15B6A /* prmcw3.h */, + 2213454C1DB0386600E14202 /* prmc.h */, 31EEAC0B156AB27B00714D05 /* protocol.c */, 311F2F7117398B7100C15B6A /* protocol.h */, - 311F2F7217398B7100C15B6A /* pthrdext.h */, 2291A5EB175CB53E001D4920 /* range.c */, 2291A5EC175CB53E001D4920 /* range.h */, + 2239BB4C20EE2E34007AC917 /* rangetree.c */, + 2239BB4D20EE2E4D007AC917 /* rangetree.h */, 31EEAC1B156AB2B200714D05 /* ref.c */, - 31EEAC0C156AB27B00714D05 /* reserv.c */, 31EEAC30156AB2F200714D05 /* ring.c */, 311F2F7317398B7100C15B6A /* ring.h */, 31EEAC1C156AB2B200714D05 /* root.c */, @@ -2603,7 +2605,6 @@ 3112ED3A18ABC57F00CC531A /* sa.h */, 31EEAC31156AB2F200714D05 /* sac.c */, 311F2F7417398B7100C15B6A /* sac.h */, - 311F2F7517398B8E00C15B6A /* sc.h */, 314562191C72ABFA00D7A514 /* scan.c */, 31EEAC1D156AB2B200714D05 /* seg.c */, 31EEAC32156AB2F200714D05 /* shield.c */, @@ -2612,7 +2613,6 @@ 22FACEDA1888088A000FDBC1 /* ss.c */, 311F2F7717398B8E00C15B6A /* ss.h */, 311F2F7817398B8E00C15B6A /* th.h */, - 311F2F7917398B8E00C15B6A /* thw3.h */, 31EEAC1E156AB2B200714D05 /* trace.c */, 31EEAC1F156AB2B200714D05 /* traceanc.c */, 31EEAC0D156AB27B00714D05 /* tract.c */, @@ -2630,18 +2630,19 @@ 31EEAC4B156AB39C00714D05 /* Platform */ = { isa = PBXGroup; children = ( - 315B7AFC17834FDB00B097C4 /* proti3.c */, - 315B7AFD17834FDB00B097C4 /* proti6.c */, 31EEAC4C156AB3B000714D05 /* lockix.c */, - 31172ABB177512F6009488E5 /* prmci3xc.c */, - 31172ABC1775131C009488E5 /* prmci6xc.c */, + 315B7AFC17834FDB00B097C4 /* prmci3.c */, + 311F2F6D17398B6300C15B6A /* prmci3.h */, + 315B7AFD17834FDB00B097C4 /* prmci6.c */, + 311F2F6E17398B6300C15B6A /* prmci6.h */, + 2213454D1DB038D400E14202 /* prmcxc.c */, + 31172ABB177512F6009488E5 /* prmcxci3.c */, + 31172ABC1775131C009488E5 /* prmcxci6.c */, 31172ABE1775164F009488E5 /* prmcxc.h */, 31EEAC4F156AB3E300714D05 /* protix.c */, 31C83ADD1786281C0031A0DB /* protxc.h */, 31172AC017752253009488E5 /* protxc.c */, 31EEACA7156AB79800714D05 /* span.c */, - 31A47BA5156C1E5E0039B1C2 /* ssixi3.c */, - 3104AFA5156D27E7000A585A /* ssixi6.c */, 31172ABA17750F9D009488E5 /* thxc.c */, 31EEAC53156AB3E300714D05 /* vmix.c */, ); @@ -2662,9 +2663,9 @@ 31CD33BD173A9F1500524741 /* poolams.h */, 3124CACE156BE4CF00753214 /* poolawl.c */, 3124CACA156BE4A300753214 /* poollo.c */, + 31EEAC2F156AB2F200714D05 /* poolmv.c */, 31D4D5FD1745058100BE84B5 /* poolmv2.c */, 2291A5A8175CAA51001D4920 /* poolmv2.h */, - 31EEAC5F156AB44D00714D05 /* poolmvff.c */, 31EEAC5D156AB43F00714D05 /* poolsnc.c */, ); name = "Extra pools"; @@ -2737,6 +2738,24 @@ productReference = 2231BB6718CA97DC002D6322 /* locusss */; productType = "com.apple.product-type.tool"; }; + 223E795819EAB00B00DC26A6 /* sncss */ = { + isa = PBXNativeTarget; + buildConfigurationList = 223E796119EAB00B00DC26A6 /* Build configuration list for PBXNativeTarget "sncss" */; + buildPhases = ( + 223E795B19EAB00B00DC26A6 /* Sources */, + 223E795E19EAB00B00DC26A6 /* Frameworks */, + 223E796019EAB00B00DC26A6 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + 223E795919EAB00B00DC26A6 /* PBXTargetDependency */, + ); + name = sncss; + productName = apss; + productReference = 223E796519EAB00B00DC26A6 /* sncss */; + productType = "com.apple.product-type.tool"; + }; 224CC78C175E1821002FF81B /* fotest */ = { isa = PBXNativeTarget; buildConfigurationList = 224CC795175E1821002FF81B /* Build configuration list for PBXNativeTarget "fotest" */; @@ -2844,6 +2863,24 @@ productReference = 22C2ACAF18BE400A006B3677 /* nailboardtest */; productType = "com.apple.product-type.tool"; }; + 22EA3F3820D2B0D90065F5B6 /* forktest */ = { + isa = PBXNativeTarget; + buildConfigurationList = 22EA3F4120D2B0D90065F5B6 /* Build configuration list for PBXNativeTarget "forktest" */; + buildPhases = ( + 22EA3F3B20D2B0D90065F5B6 /* Sources */, + 22EA3F3E20D2B0D90065F5B6 /* Frameworks */, + 22EA3F4020D2B0D90065F5B6 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + 22EA3F3920D2B0D90065F5B6 /* PBXTargetDependency */, + ); + name = forktest; + productName = mv2test; + productReference = 22EA3F4520D2B0D90065F5B6 /* forktest */; + productType = "com.apple.product-type.tool"; + }; 22F846B018F437B900982BA7 /* lockut */ = { isa = PBXNativeTarget; buildConfigurationList = 22F846B918F437B900982BA7 /* Build configuration list for PBXNativeTarget "lockut" */; @@ -3604,6 +3641,7 @@ 2291A5C1175CAFCA001D4920 /* expt825 */, 3114A5BC156E9315001E0AA3 /* finalcv */, 3114A5D5156E93A0001E0AA3 /* finaltest */, + 22EA3F3820D2B0D90065F5B6 /* forktest */, 224CC78C175E1821002FF81B /* fotest */, 6313D46718A400B200EB03EF /* gcbench */, 3114A64B156E9596001E0AA3 /* landtest */, @@ -3621,6 +3659,7 @@ 3114A5A6156E92C0001E0AA3 /* qs */, 3104AFC7156D35E2000A585A /* sacss */, 3104B03C156D3AD7000A585A /* segsmss */, + 223E795819EAB00B00DC26A6 /* sncss */, 31D6008B156D402900337B26 /* steptest */, 3114A61B156E9485001E0AA3 /* teletest */, 3114A6AB156E9759001E0AA3 /* walkt0 */, @@ -3728,6 +3767,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 223E795B19EAB00B00DC26A6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 223E796719EAB05C00DC26A6 /* sncss.c in Sources */, + 223E795D19EAB00B00DC26A6 /* testlib.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 224CC78F175E1821002FF81B /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -3793,6 +3841,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 22EA3F3B20D2B0D90065F5B6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 22EA3F4620D2B0FD0065F5B6 /* forktest.c in Sources */, + 22EA3F3D20D2B0D90065F5B6 /* testlib.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 22F846B318F437B900982BA7 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -4063,6 +4120,7 @@ 3124CAC8156BE48D00753214 /* fmtdy.c in Sources */, 3124CAC9156BE48D00753214 /* fmtdytst.c in Sources */, 3124CACD156BE4C200753214 /* fmtno.c in Sources */, + 311A44F81C8B1EBD00852E2B /* testthrix.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -4106,6 +4164,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 311A44F91C8B1EC200852E2B /* testthrix.c in Sources */, 31D60018156D3CC300337B26 /* awluthe.c in Sources */, 31D6001A156D3CDC00337B26 /* fmtdy.c in Sources */, 31D6001B156D3CDC00337B26 /* fmtdytst.c in Sources */, @@ -4253,6 +4312,11 @@ target = 2231BB5A18CA97DC002D6322 /* locusss */; targetProxy = 2231BB6E18CA986D002D6322 /* PBXContainerItemProxy */; }; + 223E795919EAB00B00DC26A6 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 31EEABFA156AAF9D00714D05 /* mps */; + targetProxy = 223E795A19EAB00B00DC26A6 /* PBXContainerItemProxy */; + }; 224CC78D175E1821002FF81B /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 31EEABFA156AAF9D00714D05 /* mps */; @@ -4303,6 +4367,11 @@ target = 2291A5C1175CAFCA001D4920 /* expt825 */; targetProxy = 2291A5E7175CB20E001D4920 /* PBXContainerItemProxy */; }; + 229E228819EAB10D00E21417 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 223E795819EAB00B00DC26A6 /* sncss */; + targetProxy = 229E228719EAB10D00E21417 /* PBXContainerItemProxy */; + }; 22B2BC3918B643AD00C33E63 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 31FCAE0917692403008C034C /* scheme */; @@ -4338,6 +4407,16 @@ target = 3104AFF1156D37A0000A585A /* all */; targetProxy = 22CDE92D16E9EB9300366D0A /* PBXContainerItemProxy */; }; + 22EA3F3920D2B0D90065F5B6 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 31EEABFA156AAF9D00714D05 /* mps */; + targetProxy = 22EA3F3A20D2B0D90065F5B6 /* PBXContainerItemProxy */; + }; + 22EA3F4820D2B23F0065F5B6 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 22EA3F3820D2B0D90065F5B6 /* forktest */; + targetProxy = 22EA3F4720D2B23F0065F5B6 /* PBXContainerItemProxy */; + }; 22F846B118F437B900982BA7 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 31EEABFA156AAF9D00714D05 /* mps */; @@ -4812,6 +4891,27 @@ }; name = RASH; }; + 223E796219EAB00B00DC26A6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 223E796319EAB00B00DC26A6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 223E796419EAB00B00DC26A6 /* RASH */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = RASH; + }; 224CC796175E1821002FF81B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -4931,6 +5031,27 @@ }; name = Release; }; + 22EA3F4220D2B0D90065F5B6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 22EA3F4320D2B0D90065F5B6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 22EA3F4420D2B0D90065F5B6 /* RASH */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = RASH; + }; 22F846BA18F437B900982BA7 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -5390,7 +5511,7 @@ CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_C_LANGUAGE_STANDARD = ansi; + GCC_C_LANGUAGE_STANDARD = c89; GCC_OPTIMIZATION_LEVEL = s; GCC_PREPROCESSOR_DEFINITIONS = CONFIG_VAR_RASH; GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; @@ -5417,7 +5538,24 @@ SDKROOT = macosx; SYMROOT = xc; WARNING_CFLAGS = ( - "-pedantic\n-Waggregate-return\n-Wall\n-Wcast-qual\n-Wconversion\n-Wduplicate-enum\n-Wextra\n-Winline\n-Wmissing-prototypes\n-Wmissing-variable-declarations\n-Wnested-externs\n-Wno-extended-offsetof\n-Wpointer-arith\n-Wshadow\n-Wstrict-aliasing=2\n-Wstrict-prototypes\n-Wunreachable-code\n-Wwrite-strings\n", + "-pedantic", + "-Waggregate-return", + "-Wall", + "-Wcast-qual", + "-Wconversion", + "-Wduplicate-enum", + "-Wextra", + "-Winline", + "-Wmissing-prototypes", + "-Wmissing-variable-declarations", + "-Wnested-externs", + "-Wno-extended-offsetof", + "-Wpointer-arith", + "-Wshadow", + "-Wstrict-aliasing=2", + "-Wstrict-prototypes", + "-Wunreachable-code", + "-Wwrite-strings", ); }; name = RASH; @@ -5812,7 +5950,7 @@ CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - GCC_C_LANGUAGE_STANDARD = ansi; + GCC_C_LANGUAGE_STANDARD = c89; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = CONFIG_VAR_COOL; GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; @@ -5840,7 +5978,24 @@ SDKROOT = macosx; SYMROOT = xc; WARNING_CFLAGS = ( - "-pedantic\n-Waggregate-return\n-Wall\n-Wcast-qual\n-Wconversion\n-Wduplicate-enum\n-Wextra\n-Winline\n-Wmissing-prototypes\n-Wmissing-variable-declarations\n-Wnested-externs\n-Wno-extended-offsetof\n-Wpointer-arith\n-Wshadow\n-Wstrict-aliasing=2\n-Wstrict-prototypes\n-Wunreachable-code\n-Wwrite-strings\n", + "-pedantic", + "-Waggregate-return", + "-Wall", + "-Wcast-qual", + "-Wconversion", + "-Wduplicate-enum", + "-Wextra", + "-Winline", + "-Wmissing-prototypes", + "-Wmissing-variable-declarations", + "-Wnested-externs", + "-Wno-extended-offsetof", + "-Wpointer-arith", + "-Wshadow", + "-Wstrict-aliasing=2", + "-Wstrict-prototypes", + "-Wunreachable-code", + "-Wwrite-strings", ); }; name = Debug; @@ -5854,7 +6009,7 @@ CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_C_LANGUAGE_STANDARD = ansi; + GCC_C_LANGUAGE_STANDARD = c89; GCC_OPTIMIZATION_LEVEL = s; GCC_PREPROCESSOR_DEFINITIONS = CONFIG_VAR_HOT; GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; @@ -5881,7 +6036,24 @@ SDKROOT = macosx; SYMROOT = xc; WARNING_CFLAGS = ( - "-pedantic\n-Waggregate-return\n-Wall\n-Wcast-qual\n-Wconversion\n-Wduplicate-enum\n-Wextra\n-Winline\n-Wmissing-prototypes\n-Wmissing-variable-declarations\n-Wnested-externs\n-Wno-extended-offsetof\n-Wpointer-arith\n-Wshadow\n-Wstrict-aliasing=2\n-Wstrict-prototypes\n-Wunreachable-code\n-Wwrite-strings\n", + "-pedantic", + "-Waggregate-return", + "-Wall", + "-Wcast-qual", + "-Wconversion", + "-Wduplicate-enum", + "-Wextra", + "-Winline", + "-Wmissing-prototypes", + "-Wmissing-variable-declarations", + "-Wnested-externs", + "-Wno-extended-offsetof", + "-Wpointer-arith", + "-Wshadow", + "-Wstrict-aliasing=2", + "-Wstrict-prototypes", + "-Wunreachable-code", + "-Wwrite-strings", ); }; name = Release; @@ -6016,6 +6188,16 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 223E796119EAB00B00DC26A6 /* Build configuration list for PBXNativeTarget "sncss" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 223E796219EAB00B00DC26A6 /* Debug */, + 223E796319EAB00B00DC26A6 /* Release */, + 223E796419EAB00B00DC26A6 /* RASH */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 224CC795175E1821002FF81B /* Build configuration list for PBXNativeTarget "fotest" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -6086,6 +6268,16 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 22EA3F4120D2B0D90065F5B6 /* Build configuration list for PBXNativeTarget "forktest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 22EA3F4220D2B0D90065F5B6 /* Debug */, + 22EA3F4320D2B0D90065F5B6 /* Release */, + 22EA3F4420D2B0D90065F5B6 /* RASH */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 22F846B918F437B900982BA7 /* Build configuration list for PBXNativeTarget "lockut" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/mps/code/mpsi.c b/mps/code/mpsi.c index b85cb3b4862..479d591a8cb 100644 --- a/mps/code/mpsi.c +++ b/mps/code/mpsi.c @@ -1,7 +1,7 @@ /* mpsi.c: MEMORY POOL SYSTEM C INTERFACE LAYER * * $Id$ - * Copyright (c) 2001-2015 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 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, @@ -234,6 +234,25 @@ size_t mps_arena_spare_commit_limit(mps_arena_t arena) return limit; } +double mps_arena_pause_time(mps_arena_t arena) +{ + double pause_time; + + ArenaEnter(arena); + pause_time = ArenaPauseTime(arena); + ArenaLeave(arena); + + return pause_time; +} + +void mps_arena_pause_time_set(mps_arena_t arena, double pause_time) +{ + ArenaEnter(arena); + ArenaSetPauseTime(arena, pause_time); + ArenaLeave(arena); +} + + void mps_arena_clamp(mps_arena_t arena) { ArenaEnter(arena); @@ -245,7 +264,9 @@ void mps_arena_clamp(mps_arena_t arena) void mps_arena_release(mps_arena_t arena) { ArenaEnter(arena); - ArenaRelease(ArenaGlobals(arena)); + STACK_CONTEXT_BEGIN(arena) { + ArenaRelease(ArenaGlobals(arena)); + } STACK_CONTEXT_END(arena); ArenaLeave(arena); } @@ -258,6 +279,15 @@ void mps_arena_park(mps_arena_t arena) } +void mps_arena_postmortem(mps_arena_t arena) +{ + /* Don't call ArenaEnter -- one of the purposes of this function is + * to release the arena lock if it's held */ + AVER(TESTT(Arena, arena)); + ArenaPostmortem(ArenaGlobals(arena)); +} + + void mps_arena_expose(mps_arena_t arena) { ArenaEnter(arena); @@ -285,7 +315,10 @@ mps_res_t mps_arena_start_collect(mps_arena_t arena) { Res res; ArenaEnter(arena); - res = ArenaStartCollect(ArenaGlobals(arena), TraceStartWhyCLIENTFULL_INCREMENTAL); + STACK_CONTEXT_BEGIN(arena) { + res = ArenaStartCollect(ArenaGlobals(arena), + TraceStartWhyCLIENTFULL_INCREMENTAL); + } STACK_CONTEXT_END(arena); ArenaLeave(arena); return (mps_res_t)res; } @@ -294,7 +327,9 @@ mps_res_t mps_arena_collect(mps_arena_t arena) { Res res; ArenaEnter(arena); - res = ArenaCollect(ArenaGlobals(arena), TraceStartWhyCLIENTFULL_BLOCK); + STACK_CONTEXT_BEGIN(arena) { + res = ArenaCollect(ArenaGlobals(arena), TraceStartWhyCLIENTFULL_BLOCK); + } STACK_CONTEXT_END(arena); ArenaLeave(arena); return (mps_res_t)res; } @@ -305,7 +340,9 @@ mps_bool_t mps_arena_step(mps_arena_t arena, { Bool b; ArenaEnter(arena); - b = ArenaStep(ArenaGlobals(arena), interval, multiplier); + STACK_CONTEXT_BEGIN(arena) { + b = ArenaStep(ArenaGlobals(arena), interval, multiplier); + } STACK_CONTEXT_END(arena); ArenaLeave(arena); return b; } @@ -372,6 +409,17 @@ void mps_arena_destroy(mps_arena_t arena) } +/* mps_arena_busy -- is the arena part way through an operation? */ + +mps_bool_t mps_arena_busy(mps_arena_t arena) +{ + /* Don't call ArenaEnter -- the purpose of this function is to + * determine if the arena lock is held */ + AVER(TESTT(Arena, arena)); + return ArenaBusy(arena); +} + + /* mps_arena_has_addr -- is this address managed by this arena? */ mps_bool_t mps_arena_has_addr(mps_arena_t arena, mps_addr_t p) @@ -743,24 +791,24 @@ mps_res_t mps_alloc(mps_addr_t *p_o, mps_pool_t pool, size_t size) Addr p; Res res; - AVER(TESTT(Pool, pool)); + AVER_CRITICAL(TESTT(Pool, pool)); arena = PoolArena(pool); ArenaEnter(arena); + STACK_CONTEXT_BEGIN(arena) { - ArenaPoll(ArenaGlobals(arena)); /* .poll */ + ArenaPoll(ArenaGlobals(arena)); /* .poll */ - AVER(p_o != NULL); - AVERT(Pool, pool); - AVER(size > 0); - /* Note: class may allow unaligned size, see */ - /* . */ - /* Rest ignored, see .varargs. */ + AVER_CRITICAL(p_o != NULL); + AVERT_CRITICAL(Pool, pool); + AVER_CRITICAL(size > 0); + /* Note: class may allow unaligned size, see */ + /* . */ + /* Rest ignored, see .varargs. */ - /* @@@@ There is currently no requirement for reservoirs to work */ - /* with unbuffered allocation. */ - res = PoolAlloc(&p, pool, size, FALSE); + res = PoolAlloc(&p, pool, size); + } STACK_CONTEXT_END(arena); ArenaLeave(arena); if (res != ResOK) @@ -787,15 +835,15 @@ void mps_free(mps_pool_t pool, mps_addr_t p, size_t size) { Arena arena; - AVER(TESTT(Pool, pool)); + AVER_CRITICAL(TESTT(Pool, pool)); arena = PoolArena(pool); ArenaEnter(arena); - AVERT(Pool, pool); - AVER(size > 0); + AVERT_CRITICAL(Pool, pool); + AVER_CRITICAL(size > 0); /* Note: class may allow unaligned size, see */ - /* . */ + /* . */ PoolFree(pool, (Addr)p, size); ArenaLeave(arena); @@ -911,17 +959,7 @@ mps_res_t (mps_reserve)(mps_addr_t *p_o, mps_ap_t mps_ap, size_t size) mps_res_t mps_reserve_with_reservoir_permit(mps_addr_t *p_o, mps_ap_t mps_ap, size_t size) { - mps_res_t res; - - AVER(p_o != NULL); - AVER(size > 0); - AVER(mps_ap != NULL); - AVER(TESTT(Buffer, BufferOfAP(mps_ap))); - AVER(mps_ap->init == mps_ap->alloc); - - MPS_RESERVE_WITH_RESERVOIR_PERMIT_BLOCK(res, *p_o, mps_ap, size); - - return res; + return mps_reserve(p_o, mps_ap, size); } @@ -968,7 +1006,7 @@ mps_res_t (mps_ap_frame_push)(mps_frame_t *frame_o, mps_ap_t mps_ap) return MPS_RES_FAIL; } - if (!mps_ap->_lwpoppending) { + if (mps_ap->init < mps_ap->limit) { /* Valid state for a lightweight push */ *frame_o = (mps_frame_t)mps_ap->init; return MPS_RES_OK; @@ -1001,6 +1039,9 @@ mps_res_t (mps_ap_frame_push)(mps_frame_t *frame_o, mps_ap_t mps_ap) mps_res_t (mps_ap_frame_pop)(mps_ap_t mps_ap, mps_frame_t frame) { + Buffer buf; + Pool pool; + AVER(mps_ap != NULL); /* Can't check frame because it's an arbitrary value */ @@ -1009,20 +1050,27 @@ mps_res_t (mps_ap_frame_pop)(mps_ap_t mps_ap, mps_frame_t frame) return MPS_RES_FAIL; } - if (mps_ap->_enabled) { - /* Valid state for a lightweight pop */ - mps_ap->_frameptr = (mps_addr_t)frame; /* record pending pop */ - mps_ap->_lwpoppending = TRUE; - mps_ap->limit = (mps_addr_t)0; /* trap the buffer */ + buf = BufferOfAP(mps_ap); + AVER(TESTT(Buffer, buf)); + pool = buf->pool; + AVER(TESTT(Pool, pool)); + + /* It's not thread-safe to read BufferBase here in an automatically + * managed pool (see job003947), so test AttrGC first. */ + if (!PoolHasAttr(pool, AttrGC) + && BufferBase(buf) <= (Addr)frame + && (mps_addr_t)frame < mps_ap->init) + { + /* Lightweight pop to earlier address in same buffer in a manually + * managed pool. */ + mps_ap->init = mps_ap->alloc = (mps_addr_t)frame; return MPS_RES_OK; } else { /* Need a heavyweight pop */ - Buffer buf = BufferOfAP(mps_ap); Arena arena; Res res; - AVER(TESTT(Buffer, buf)); arena = BufferArena(buf); ArenaEnter(arena); @@ -1054,16 +1102,18 @@ mps_res_t mps_ap_fill(mps_addr_t *p_o, mps_ap_t mps_ap, size_t size) arena = BufferArena(buf); ArenaEnter(arena); + STACK_CONTEXT_BEGIN(arena) { - ArenaPoll(ArenaGlobals(arena)); /* .poll */ + ArenaPoll(ArenaGlobals(arena)); /* .poll */ - AVER(p_o != NULL); - AVERT(Buffer, buf); - AVER(size > 0); - AVER(SizeIsAligned(size, BufferPool(buf)->alignment)); + AVER(p_o != NULL); + AVERT(Buffer, buf); + AVER(size > 0); + AVER(SizeIsAligned(size, BufferPool(buf)->alignment)); /* */ - res = BufferFill(&p, buf, size, FALSE); + res = BufferFill(&p, buf, size); + } STACK_CONTEXT_END(arena); ArenaLeave(arena); if (res != ResOK) @@ -1076,32 +1126,7 @@ mps_res_t mps_ap_fill(mps_addr_t *p_o, mps_ap_t mps_ap, size_t size) mps_res_t mps_ap_fill_with_reservoir_permit(mps_addr_t *p_o, mps_ap_t mps_ap, size_t size) { - Buffer buf = BufferOfAP(mps_ap); - Arena arena; - Addr p; - Res res; - - AVER(mps_ap != NULL); - AVER(TESTT(Buffer, buf)); - arena = BufferArena(buf); - - ArenaEnter(arena); - - ArenaPoll(ArenaGlobals(arena)); /* .poll */ - - AVER(p_o != NULL); - AVERT(Buffer, buf); - AVER(size > 0); - AVER(SizeIsAligned(size, BufferPool(buf)->alignment)); - - res = BufferFill(&p, buf, size, TRUE); - - ArenaLeave(arena); - - if (res != ResOK) - return (mps_res_t)res; - *p_o = (mps_addr_t)p; - return MPS_RES_OK; + return mps_ap_fill(p_o, mps_ap, size); } @@ -1210,10 +1235,11 @@ mps_res_t mps_sac_fill(mps_addr_t *p_o, mps_sac_t mps_sac, size_t size, AVER(p_o != NULL); AVER(TESTT(SAC, sac)); arena = SACArena(sac); + UNUSED(has_reservoir_permit); /* deprecated */ ArenaEnter(arena); - res = SACFill(&p, sac, size, (has_reservoir_permit != 0)); + res = SACFill(&p, sac, size); ArenaLeave(arena); @@ -1553,7 +1579,7 @@ mps_res_t mps_root_create_thread_tagged(mps_root_t *mps_root_o, /* See .root-mode. */ res = RootCreateThreadTagged(&root, arena, rank, thread, scan_area, mask, pattern, - (Word *)cold); + cold); ArenaLeave(arena); @@ -1703,7 +1729,7 @@ mps_res_t mps_fix(mps_ss_t mps_ss, mps_addr_t *ref_io) mps_res_t res; MPS_SCAN_BEGIN(mps_ss) { - res = MPS_FIX(mps_ss, ref_io); + res = MPS_FIX12(mps_ss, ref_io); } MPS_SCAN_END(mps_ss); return res; @@ -2035,56 +2061,56 @@ mps_res_t mps_ap_alloc_pattern_begin(mps_ap_t mps_ap, mps_res_t mps_ap_alloc_pattern_end(mps_ap_t mps_ap, mps_alloc_pattern_t alloc_pattern) { - Buffer buf; Arena arena; Res res; AVER(mps_ap != NULL); - buf = BufferOfAP(mps_ap); - AVER(TESTT(Buffer, buf)); + AVER(TESTT(Buffer, BufferOfAP(mps_ap))); UNUSED(alloc_pattern); /* .ramp.hack */ - arena = BufferArena(buf); + arena = BufferArena(BufferOfAP(mps_ap)); + ArenaEnter(arena); - - res = BufferRampEnd(buf); - ArenaPoll(ArenaGlobals(arena)); /* .poll */ - + STACK_CONTEXT_BEGIN(arena) { + res = BufferRampEnd(BufferOfAP(mps_ap)); + ArenaPoll(ArenaGlobals(arena)); /* .poll */ + } STACK_CONTEXT_END(arena); ArenaLeave(arena); + return (mps_res_t)res; } mps_res_t mps_ap_alloc_pattern_reset(mps_ap_t mps_ap) { - Buffer buf; Arena arena; AVER(mps_ap != NULL); - buf = BufferOfAP(mps_ap); - AVER(TESTT(Buffer, buf)); + AVER(TESTT(Buffer, BufferOfAP(mps_ap))); + + arena = BufferArena(BufferOfAP(mps_ap)); - arena = BufferArena(buf); ArenaEnter(arena); - - BufferRampReset(buf); - ArenaPoll(ArenaGlobals(arena)); /* .poll */ - + STACK_CONTEXT_BEGIN(arena) { + BufferRampReset(BufferOfAP(mps_ap)); + ArenaPoll(ArenaGlobals(arena)); /* .poll */ + } STACK_CONTEXT_END(arena); ArenaLeave(arena); + return MPS_RES_OK; } -/* Low memory reservoir */ +/* Low memory reservoir (deprecated -- see job003985) */ /* mps_reservoir_limit_set -- set the reservoir size */ void mps_reservoir_limit_set(mps_arena_t arena, size_t size) { - ArenaEnter(arena); - ReservoirSetLimit(ArenaReservoir(arena), size); - ArenaLeave(arena); + UNUSED(arena); + UNUSED(size); + NOOP; } @@ -2092,14 +2118,8 @@ void mps_reservoir_limit_set(mps_arena_t arena, size_t size) size_t mps_reservoir_limit(mps_arena_t arena) { - Size size; - - ArenaEnter(arena); - - size = ReservoirLimit(ArenaReservoir(arena)); - - ArenaLeave(arena); - return size; + UNUSED(arena); + return 0; } @@ -2107,14 +2127,8 @@ size_t mps_reservoir_limit(mps_arena_t arena) size_t mps_reservoir_available(mps_arena_t arena) { - Size size; - - ArenaEnter(arena); - - size = ReservoirAvailable(ArenaReservoir(arena)); - - ArenaLeave(arena); - return size; + UNUSED(arena); + return 0; } @@ -2177,7 +2191,7 @@ void _mps_args_set_key(mps_arg_s args[MPS_ARGS_MAX], unsigned i, /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2015 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/mpsicv.c b/mps/code/mpsicv.c index 7eae7eb497c..04bc7fcc54c 100644 --- a/mps/code/mpsicv.c +++ b/mps/code/mpsicv.c @@ -1,7 +1,7 @@ /* mpsicv.c: MPSI COVERAGE TEST * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. * Portions copyright (c) 2002 Global Graphics Software. */ @@ -9,7 +9,7 @@ #include "mpslib.h" #include "mpscamc.h" #include "mpsavm.h" -#include "mpscmv.h" +#include "mpscmvff.h" #include "fmthe.h" #include "fmtdy.h" #include "fmtdytst.h" @@ -96,9 +96,14 @@ static void alignmentTest(mps_arena_t arena) int dummy = 0; size_t j, size; - die(mps_pool_create(&pool, arena, mps_class_mv(), - (size_t)0x1000, (size_t)1024, (size_t)16384), - "alignment pool create"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_EXTEND_BY, 0x1000); + MPS_ARGS_ADD(args, MPS_KEY_MEAN_SIZE, 1024); + MPS_ARGS_ADD(args, MPS_KEY_MAX_SIZE, 16384); + die(mps_pool_create_k(&pool, arena, mps_class_mvff(), args), + "alignment pool create"); + } MPS_ARGS_END(args); + size = max(sizeof(double), sizeof(long)); #ifdef HAS_LONG_LONG size = max(size, sizeof(long_long_t)); @@ -143,31 +148,6 @@ static mps_addr_t make(void) } -/* make_with_permit -- allocate an object, with reservoir permit */ - -static mps_addr_t make_with_permit(void) -{ - size_t length = rnd() % 20; - size_t sizeCli = (length+2)*sizeof(mps_word_t); - size_t sizeMps = SizeCli2Mps(sizeCli); - mps_addr_t pMps, pCli; - mps_res_t res; - - do { - MPS_RESERVE_WITH_RESERVOIR_PERMIT_BLOCK(res, pMps, ap, sizeMps); - 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"); - } while(!mps_commit(ap, pMps, sizeMps)); - - return pCli; -} - - /* make_no_inline -- allocate an object, using non-inlined interface */ static mps_addr_t make_no_inline(void) @@ -325,7 +305,7 @@ static mps_res_t root_single(mps_ss_t ss, void *p, size_t s) * incidentally tests: * mps_alloc * mps_arena_commit_limit_set - * mps_class_mv + * mps_class_mvff * mps_pool_create * mps_pool_destroy */ @@ -339,9 +319,14 @@ static void arena_commit_test(mps_arena_t arena) void *p; mps_res_t res; - die(mps_pool_create(&pool, arena, mps_class_mv(), - (size_t)0x1000, (size_t)1024, (size_t)16384), - "commit pool create"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_EXTEND_BY, 0x1000); + MPS_ARGS_ADD(args, MPS_KEY_MEAN_SIZE, 1024); + MPS_ARGS_ADD(args, MPS_KEY_MAX_SIZE, 16384); + die(mps_pool_create_k(&pool, arena, mps_class_mvff(), args), + "commit pool create"); + } MPS_ARGS_END(args); + limit = mps_arena_commit_limit(arena); committed = mps_arena_committed(arena); reserved = mps_arena_reserved(arena); @@ -358,36 +343,13 @@ static void arena_commit_test(mps_arena_t arena) } -/* reservoir_test -- Test the reservoir interface - * - * This has not been tuned to actually dip into the reservoir. See - * QA test 132 for that. - */ - -#define reservoirSIZE ((size_t)128 * 1024) - -static void reservoir_test(mps_arena_t arena) -{ - (void)make_with_permit(); - cdie(mps_reservoir_available(arena) == 0, "empty reservoir"); - cdie(mps_reservoir_limit(arena) == 0, "no reservoir"); - mps_reservoir_limit_set(arena, reservoirSIZE); - cdie(mps_reservoir_limit(arena) >= reservoirSIZE, "reservoir limit set"); - cdie(mps_reservoir_available(arena) >= reservoirSIZE, "got reservoir"); - (void)make_with_permit(); - mps_reservoir_limit_set(arena, 0); - cdie(mps_reservoir_available(arena) == 0, "empty reservoir"); - cdie(mps_reservoir_limit(arena) == 0, "no reservoir"); - (void)make_with_permit(); -} - - static void *test(void *arg, size_t s) { mps_arena_t arena; mps_fmt_t format; mps_chain_t chain; - mps_root_t exactRoot, ambigRoot, singleRoot, fmtRoot; + mps_root_t exactAreaRoot, exactTableRoot, ambigAreaRoot, ambigTableRoot, + singleRoot, fmtRoot; unsigned long i; /* Leave arena clamped until we have allocated this many objects. is 0 when arena has not been clamped. */ @@ -418,9 +380,13 @@ static void *test(void *arg, size_t s) die(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - die(mps_pool_create(&mv, arena, mps_class_mv(), - (size_t)0x10000, (size_t)32, (size_t)0x10000), - "pool_create(mv)"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_EXTEND_BY, 0x10000); + MPS_ARGS_ADD(args, MPS_KEY_MEAN_SIZE, 32); + MPS_ARGS_ADD(args, MPS_KEY_MAX_SIZE, 0x10000); + die(mps_pool_create_k(&mv, arena, mps_class_mvff(), args), + "pool_create(mv)"); + } MPS_ARGS_END(args); pool_create_v_test(arena, format, chain); /* creates amc pool */ @@ -435,14 +401,29 @@ static void *test(void *arg, size_t s) ambigRoots[j] = rnd_addr(); } - die(mps_root_create_table_masked(&exactRoot, arena, + die(mps_root_create_area_tagged(&exactAreaRoot, arena, + mps_rank_exact(), (mps_rm_t)0, + &exactRoots[0], + &exactRoots[exactRootsCOUNT / 2], + mps_scan_area_tagged, + MPS_WORD_CONST(1), 0), + "root_create_area_tagged(exact)"); + die(mps_root_create_table_masked(&exactTableRoot, arena, mps_rank_exact(), (mps_rm_t)0, - &exactRoots[0], exactRootsCOUNT, + &exactRoots[exactRootsCOUNT / 2], + (exactRootsCOUNT + 1) / 2, MPS_WORD_CONST(1)), - "root_create_table(exact)"); - die(mps_root_create_table(&ambigRoot, arena, + "root_create_table_masked(exact)"); + die(mps_root_create_area(&ambigAreaRoot, arena, + mps_rank_ambig(), (mps_rm_t)0, + &ambigRoots[0], + &ambigRoots[ambigRootsCOUNT / 2], + mps_scan_area, NULL), + "root_create_area(ambig)"); + die(mps_root_create_table(&ambigTableRoot, arena, mps_rank_ambig(), (mps_rm_t)0, - &ambigRoots[0], ambigRootsCOUNT), + &ambigRoots[ambigRootsCOUNT / 2], + (ambigRootsCOUNT + 1) / 2), "root_create_table(ambig)"); obj = objNULL; @@ -483,11 +464,13 @@ static void *test(void *arg, size_t s) mps_word_t c; size_t r; + Insist(!mps_arena_busy(arena)); + c = mps_collections(arena); if(collections != c) { collections = c; - printf("\nCollection %"PRIuLONGEST", %lu objects.\n", (ulongest_t)c, i); + printf("Collection %"PRIuLONGEST", %lu objects.\n", (ulongest_t)c, i); for(r = 0; r < exactRootsCOUNT; ++r) { cdie(exactRoots[r] == objNULL || dylan_check(exactRoots[r]), "all roots check"); @@ -555,7 +538,6 @@ static void *test(void *arg, size_t s) } arena_commit_test(arena); - reservoir_test(arena); alignmentTest(arena); die(mps_arena_collect(arena), "collect"); @@ -569,8 +551,10 @@ static void *test(void *arg, size_t s) mps_ap_destroy(ap); mps_root_destroy(fmtRoot); mps_root_destroy(singleRoot); - mps_root_destroy(exactRoot); - mps_root_destroy(ambigRoot); + mps_root_destroy(exactAreaRoot); + mps_root_destroy(exactTableRoot); + mps_root_destroy(ambigAreaRoot); + mps_root_destroy(ambigTableRoot); mps_pool_destroy(amcpool); mps_chain_destroy(chain); mps_fmt_destroy(format); @@ -592,25 +576,48 @@ int main(int argc, char *argv[]) testlib_init(argc, argv); - die(mps_arena_create(&arena, mps_arena_class_vm(), TEST_ARENA_SIZE), - "arena_create"); + MPS_ARGS_BEGIN(args) { + /* Randomize pause time as a regression test for job004011. */ + MPS_ARGS_ADD(args, MPS_KEY_PAUSE_TIME, rnd_pause_time()); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, TEST_ARENA_SIZE); + 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"); - if (rnd() % 2) { + switch (rnd() % 3) { + default: + case 0: die(mps_root_create_reg(®_root, arena, mps_rank_ambig(), (mps_rm_t)0, thread, &mps_stack_scan_ambig, marker, (size_t)0), "root_create_reg"); - } else { + break; + case 1: die(mps_root_create_thread(®_root, arena, thread, marker), "root_create_thread"); + break; + case 2: + die(mps_root_create_thread_scanned(®_root, arena, mps_rank_ambig(), + (mps_rm_t)0, thread, mps_scan_area, + NULL, marker), + "root_create_thread"); + break; } mps_tramp(&r, test, arena, 0); - mps_root_destroy(reg_root); - mps_thread_dereg(thread); - mps_arena_destroy(arena); + switch (rnd() % 2) { + default: + case 0: + mps_root_destroy(reg_root); + mps_thread_dereg(thread); + mps_arena_destroy(arena); + break; + case 1: + mps_arena_postmortem(arena); + break; + } printf("%s: Conclusion: Failed to find any defects.\n", argv[0]); return 0; @@ -619,7 +626,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2014 Ravenbrook Limited . + * Copyright (c) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/mpsiw3.c b/mps/code/mpsiw3.c index 9ed7f9ddc7d..b3858fb88e5 100644 --- a/mps/code/mpsiw3.c +++ b/mps/code/mpsiw3.c @@ -2,12 +2,16 @@ * * $Id$ * - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. */ #include "mpm.h" -#include "mps.h" +#if !defined(MPS_OS_W3) +#error "mpsiw3.c is specific to MPS_OS_W3" +#endif + +#include "mps.h" #include "mpswin.h" SRCID(mpsiw3, "$Id$"); @@ -33,7 +37,7 @@ void mps_SEH_handler(void *p, size_t s) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/mpslib.h b/mps/code/mpslib.h index 323cfa55f5c..e8e2d8dfd95 100644 --- a/mps/code/mpslib.h +++ b/mps/code/mpslib.h @@ -1,7 +1,7 @@ /* mpslib.h: RAVENBROOK MEMORY POOL SYSTEM LIBRARY INTERFACE * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2017 Ravenbrook Limited. See end of file for license. * * .readership: MPS client application developers, MPS developers. * .sources: @@ -78,7 +78,7 @@ extern unsigned long mps_lib_telemetry_control(void); /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2017 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/mpsliban.c b/mps/code/mpsliban.c index 2e391596e03..4a9c28d7ab3 100644 --- a/mps/code/mpsliban.c +++ b/mps/code/mpsliban.c @@ -1,7 +1,7 @@ /* mpsliban.c: RAVENBROOK MEMORY POOL SYSTEM LIBRARY INTERFACE (ANSI) * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2017 Ravenbrook Limited. See end of file for license. * Portions copyright (c) 2002 Global Graphics Software. * * .purpose: The purpose of this code is @@ -81,8 +81,8 @@ static void mps_lib_assert_fail_default(const char *file, unsigned line, static mps_lib_assert_fail_t mps_lib_assert_handler = mps_lib_assert_fail_default; void mps_lib_assert_fail(const char *file, - unsigned line, - const char *condition) + unsigned line, + const char *condition) { mps_lib_assert_handler(file, line, condition); } @@ -206,7 +206,7 @@ unsigned long mps_lib_telemetry_control(void) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2017 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/mpstd.h b/mps/code/mpstd.h index 6147e2a40e7..388032eed40 100644 --- a/mps/code/mpstd.h +++ b/mps/code/mpstd.h @@ -142,6 +142,29 @@ #define MPS_PF_ALIGN 4 /* I'm just guessing. */ +/* gcc-mp-4.7 (MacPorts gcc47 4.7.4_5) 4.7.4 + * gcc -E -dM + * Note that Clang also defines __GNUC__ since it's generally GCC compatible, + * but that doesn't fit our system so we exclude Clang here. + */ + +#elif defined(__APPLE__) && defined(__x86_64__) && defined(__MACH__) \ + && defined(__GNUC__) && !defined(__clang__) +#if defined(CONFIG_PF_STRING) && ! defined(CONFIG_PF_XCI6GC) +#error "specified CONFIG_PF_... inconsistent with detected xci6gc" +#endif +#define MPS_PF_XCI6GC +#define MPS_PF_STRING "xci6gc" +#define MPS_OS_XC +#define MPS_ARCH_I6 +#define MPS_BUILD_GC +#define MPS_T_WORD unsigned long +#define MPS_T_ULONGEST unsigned long +#define MPS_WORD_WIDTH 64 +#define MPS_WORD_SHIFT 6 +#define MPS_PF_ALIGN 8 + + /* Apple clang version 3.1, clang -E -dM */ #elif defined(__APPLE__) && defined(__i386__) && defined(__MACH__) \ @@ -306,7 +329,7 @@ #define MPS_ARCH_I6 #define MPS_BUILD_LL #define MPS_T_WORD unsigned long -#define MPS_T_ULONGEST unsigned long /* FIXME: Check this for Clang */ +#define MPS_T_ULONGEST unsigned long #define MPS_WORD_WIDTH 64 #define MPS_WORD_SHIFT 6 #define MPS_PF_ALIGN 8 diff --git a/mps/code/mv2test.c b/mps/code/mv2test.c index 4670abbf076..a47a520af7f 100644 --- a/mps/code/mv2test.c +++ b/mps/code/mv2test.c @@ -1,7 +1,7 @@ /* mv2test.c: POOLMVT STRESS TEST * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. */ #include @@ -102,13 +102,15 @@ static mps_res_t stress(mps_arena_t arena, mps_align_t align, /* allocate a load of objects */ for(i=0; i. + * Copyright (c) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/nailboard.c b/mps/code/nailboard.c index 0b91ab35e13..90d26c84295 100644 --- a/mps/code/nailboard.c +++ b/mps/code/nailboard.c @@ -127,7 +127,7 @@ Res NailboardCreate(Nailboard *boardReturn, Arena arena, Align alignment, alignShift = SizeLog2((Size)alignment); nails = AddrOffset(base, limit) >> alignShift; levels = nailboardLevels(nails); - res = ControlAlloc(&p, arena, nailboardSize(nails, levels), FALSE); + res = ControlAlloc(&p, arena, nailboardSize(nails, levels)); if (res != ResOK) return res; diff --git a/mps/code/nailboardtest.c b/mps/code/nailboardtest.c index e1c071f3ac1..738d1436c40 100644 --- a/mps/code/nailboardtest.c +++ b/mps/code/nailboardtest.c @@ -1,7 +1,7 @@ /* 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. + * $Id$ + * Copyright (c) 2014-2018 Ravenbrook Limited. See end of file for license. * */ @@ -54,7 +54,7 @@ static void test(mps_arena_t arena) die(NailboardDescribe(board, mps_lib_get_stdout(), 0), "NailboardDescribe"); } -int main(int argc, char **argv) +int main(int argc, char *argv[]) { mps_arena_t arena; @@ -73,7 +73,7 @@ int main(int argc, char **argv) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2014 Ravenbrook Limited . + * Copyright (c) 2014-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/policy.c b/mps/code/policy.c index 134b8236013..e44f67ad9fa 100644 --- a/mps/code/policy.c +++ b/mps/code/policy.c @@ -1,7 +1,7 @@ /* policy.c: POLICY DECISIONS * * $Id$ - * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 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. @@ -76,14 +76,14 @@ Res PolicyAlloc(Tract *tractReturn, Arena arena, LocusPref pref, /* Plan C: Extend the arena, then try A and B again. */ if (moreZones != ZoneSetEMPTY) { - res = arena->class->grow(arena, pref, size); + res = Method(Arena, arena, grow)(arena, pref, size); /* If we can't extend because we hit the commit limit, try purging some spare committed memory and try again.*/ /* TODO: This would be a good time to *remap* VM instead of returning it to the OS. */ if (res == ResCOMMIT_LIMIT) { - if (arena->class->purgeSpare(arena, size) >= size) - res = arena->class->grow(arena, pref, size); + if (Method(Arena, arena, purgeSpare)(arena, size) >= size) + res = Method(Arena, arena, grow)(arena, pref, size); } if (res == ResOK) { if (zones != ZoneSetEMPTY) { @@ -144,9 +144,8 @@ static double policyCollectionTime(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; + if (arena->tracedTime >= 1.0) + collectionRate = arena->tracedWork / arena->tracedTime; else collectionRate = ARENA_DEFAULT_COLLECTION_RATE; collectionTime = collectableSize / collectionRate; @@ -161,37 +160,41 @@ static double policyCollectionTime(Arena arena) * 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 + * This is the policy behind mps_arena_step, and so the client + * must have provided us with 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, +Bool PolicyShouldCollectWorld(Arena arena, double availableTime, 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); + Size collectableSize; + double collectionTime, sinceLastWorldCollect; - /* 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; + AVERT(Arena, arena); + /* Can't collect the world if we're already collecting. */ + AVER(arena->busyTraces == TraceSetEMPTY); + + if (availableTime <= 0.0) + /* Can't collect the world if we're not given any time. */ + return FALSE; + + /* Don't collect the world if it's very small. */ + collectableSize = ArenaCollectable(arena); + if (collectableSize < ARENA_MINIMUM_COLLECTABLE_SIZE) + return FALSE; + + /* How long would it take to collect the world? */ + collectionTime = policyCollectionTime(arena); + + /* How long since we last collected the world? */ + sinceLastWorldCollect = ((now - arena->lastWorldCollect) / + (double) clocks_per_sec); + + /* Offered enough time, and long enough since we last did it? */ + return availableTime > collectionTime + && sinceLastWorldCollect > collectionTime / ARENA_MAX_COLLECT_FRACTION; } @@ -207,12 +210,10 @@ Bool PolicyShouldCollectWorld(Arena arena, double interval, double multiplier, 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; + AVER(mortalityReturn != NULL); AVERT(Chain, chain); AVERT(Trace, trace); @@ -228,75 +229,78 @@ static Res policyCondemnChain(double *mortalityReturn, Chain chain, Trace trace) -- topCondemnedGen; gen = &chain->gens[topCondemnedGen]; AVERT(GenDesc, gen); - genNewSize = GenDescNewSize(gen); - if (genNewSize >= gen->capacity * (Size)1024) + if (GenDescNewSize(gen) >= gen->capacity) break; } /* At this point, we've decided to condemn topCondemnedGen and all * lower generations. */ + TraceCondemnStart(trace); 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); + GenDescStartTrace(gen, trace); } - - 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; + return TraceCondemnEnd(mortalityReturn, trace); } /* PolicyStartTrace -- consider starting a trace + * + * If collectWorldAllowed is TRUE, consider starting a collection of + * the world. Otherwise, consider only starting collections of individual + * chains or generations. + * + * If a collection of the world was started, set *collectWorldReturn + * to TRUE. Otherwise leave it unchanged. * * If a trace was started, update *traceReturn and return TRUE. * Otherwise, leave *traceReturn unchanged and return FALSE. */ -Bool PolicyStartTrace(Trace *traceReturn, Arena arena) +Bool PolicyStartTrace(Trace *traceReturn, Bool *collectWorldReturn, + Arena arena, Bool collectWorldAllowed) { Res res; Trace trace; - Size sFoundation, sCondemned, sSurvivors, sConsTrace; - double tTracePerScan; /* tTrace/cScan */ - double dynamicDeferral; + double TraceWorkFactor = 0.25; + /* Fix the mortality of the world to avoid runaway feedback between the + dynamic criterion and the mortality of the arena's top generation, + leading to all traces collecting the world. This is a (hopefully) + temporary hack, pending an improved scheduling algorithm. */ + double TraceWorldMortality = 0.5; - /* 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; + AVER(traceReturn != NULL); + AVERT(Arena, arena); - if (dynamicDeferral < 0.0) { - /* Start full collection. */ - res = TraceStartCollectAll(&trace, arena, TraceStartWhyDYNAMICCRITERION); - if (res != ResOK) - goto failStart; - *traceReturn = trace; - return TRUE; - } else { + if (collectWorldAllowed) { + Size sFoundation, sCondemned, sSurvivors, sConsTrace; + double tTracePerScan; /* tTrace/cScan */ + double dynamicDeferral; + + /* Compute dynamic criterion. See strategy.lisp-machine. */ + sFoundation = (Size)0; /* condemning everything, only roots @@@@ */ + /* @@@@ sCondemned should be scannable only */ + sCondemned = ArenaCommitted(arena) - ArenaSpareCommitted(arena); + sSurvivors = (Size)(sCondemned * (1 - TraceWorldMortality)); + 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; + *collectWorldReturn = TRUE; + *traceReturn = trace; + return TRUE; + } + } + { /* Find the chain most over its capacity. */ Ring node, nextNode; double firstTime = 0.0; @@ -322,8 +326,8 @@ Bool PolicyStartTrace(Trace *traceReturn, Arena arena) res = policyCondemnChain(&mortality, firstChain, trace); if (res != ResOK) /* should try some other trace, really @@@@ */ goto failCondemn; - trace->chain = firstChain; - ChainStartGC(firstChain, trace); + if (TraceIsEmpty(trace)) + goto nothingCondemned; res = TraceStart(trace, mortality, trace->condemned * TraceWorkFactor); /* We don't expect normal GC traces to fail to start. */ AVER(res == ResOK); @@ -333,11 +337,9 @@ Bool PolicyStartTrace(Trace *traceReturn, Arena arena) } /* (dynamicDeferral > 0.0) */ return FALSE; +nothingCondemned: failCondemn: - TraceDestroy(trace); - /* This is an unlikely case, but clear the emergency flag so the next attempt - starts normally. */ - ArenaSetEmergency(arena, FALSE); + TraceDestroyInit(trace); failStart: return FALSE; } @@ -364,37 +366,49 @@ Bool PolicyPoll(Arena arena) * 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. + * moreWork and tracedWork are the results of the last call to TracePoll. */ -Bool PolicyPollAgain(Arena arena, Clock start, Size tracedSize) +Bool PolicyPollAgain(Arena arena, Clock start, Bool moreWork, Work tracedWork) { + Bool moreTime; Globals globals; double nextPollThreshold; AVERT(Arena, arena); + UNUSED(tracedWork); + + if (ArenaEmergency(arena)) + return TRUE; + + /* Is there more work to do and more time to do it in? */ + moreTime = (ClockNow() - start) < ArenaPauseTime(arena) * ClocksPerSec(); + if (moreWork && moreTime) + return TRUE; + + /* We're not going to do more work now, so calculate when to come back. */ + globals = ArenaGlobals(arena); - UNUSED(start); - - if (tracedSize == 0) { - /* No work was done. Sleep until NOW + a bit. */ - nextPollThreshold = globals->fillMutatorSize + ArenaPollALLOCTIME; - } else { + + if (moreWork) { /* We did one quantum of work; consume one unit of 'time'. */ nextPollThreshold = globals->pollThreshold + ArenaPollALLOCTIME; + } else { + /* No more work to do. Sleep until NOW + a bit. */ + nextPollThreshold = globals->fillMutatorSize + ArenaPollALLOCTIME; } /* Advance pollThreshold; check: enough precision? */ AVER(nextPollThreshold > globals->pollThreshold); globals->pollThreshold = nextPollThreshold; - return PolicyPoll(arena); + return FALSE; } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2016 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/pool.c b/mps/code/pool.c index f939bca84e9..eae3346ad67 100644 --- a/mps/code/pool.c +++ b/mps/code/pool.c @@ -1,12 +1,12 @@ /* pool.c: POOL IMPLEMENTATION * * $Id$ - * Copyright (c) 2001-2015 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2001 Global Graphics Software. * * DESIGN * - * .design: See and . + * .design: See . * * PURPOSE * @@ -16,7 +16,7 @@ * Pool and PoolClass objects (create, destroy, check, various * accessors, and other miscellaneous functions). * .purpose.dispatch: Dispatch functions that implement the generic - * function dispatch mechanism for Pool Classes (PoolAlloc, PoolFix, + * function dispatch mechanism for Pool Classes (PoolAlloc, PoolFree, * etc.). * * SOURCES @@ -35,46 +35,40 @@ SRCID(pool, "$Id$"); /* PoolClassCheck -- check a pool class */ -Bool PoolClassCheck(PoolClass class) +Bool PoolClassCheck(PoolClass klass) { - 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)); - CHECKL(FUNCHECK(class->alloc)); - CHECKL(FUNCHECK(class->free)); - CHECKL(FUNCHECK(class->bufferFill)); - CHECKL(FUNCHECK(class->bufferEmpty)); - CHECKL(FUNCHECK(class->access)); - CHECKL(FUNCHECK(class->whiten)); - CHECKL(FUNCHECK(class->grey)); - CHECKL(FUNCHECK(class->blacken)); - CHECKL(FUNCHECK(class->scan)); - CHECKL(FUNCHECK(class->fix)); - CHECKL(FUNCHECK(class->fixEmergency)); - CHECKL(FUNCHECK(class->reclaim)); - CHECKL(FUNCHECK(class->traceEnd)); - CHECKL(FUNCHECK(class->rampBegin)); - CHECKL(FUNCHECK(class->rampEnd)); - CHECKL(FUNCHECK(class->framePush)); - CHECKL(FUNCHECK(class->framePop)); - CHECKL(FUNCHECK(class->framePopPending)); - CHECKL(FUNCHECK(class->addrObject)); - CHECKL(FUNCHECK(class->walk)); - CHECKL(FUNCHECK(class->freewalk)); - CHECKL(FUNCHECK(class->bufferClass)); - CHECKL(FUNCHECK(class->describe)); - CHECKL(FUNCHECK(class->debugMixin)); - CHECKL(FUNCHECK(class->totalSize)); - CHECKL(FUNCHECK(class->freeSize)); - CHECKS(PoolClass, class); + CHECKD(InstClass, &klass->instClassStruct); + CHECKL(klass->size >= sizeof(PoolStruct)); + CHECKL(AttrCheck(klass->attr)); + CHECKL(!(klass->attr & AttrMOVINGGC) || (klass->attr & AttrGC)); + CHECKL(FUNCHECK(klass->varargs)); + CHECKL(FUNCHECK(klass->init)); + CHECKL(FUNCHECK(klass->alloc)); + CHECKL(FUNCHECK(klass->free)); + CHECKL(FUNCHECK(klass->segPoolGen)); + CHECKL(FUNCHECK(klass->bufferFill)); + CHECKL(FUNCHECK(klass->bufferEmpty)); + CHECKL(FUNCHECK(klass->rampBegin)); + CHECKL(FUNCHECK(klass->rampEnd)); + CHECKL(FUNCHECK(klass->framePush)); + CHECKL(FUNCHECK(klass->framePop)); + CHECKL(FUNCHECK(klass->freewalk)); + CHECKL(FUNCHECK(klass->bufferClass)); + CHECKL(FUNCHECK(klass->debugMixin)); + CHECKL(FUNCHECK(klass->totalSize)); + CHECKL(FUNCHECK(klass->freeSize)); + + /* Check that pool classes overide sets of related methods. */ + CHECKL((klass->init == PoolAbsInit) == + (klass->instClassStruct.finish == PoolAbsFinish)); + CHECKL((klass->bufferFill == PoolNoBufferFill) == + (klass->bufferEmpty == PoolNoBufferEmpty)); + CHECKL((klass->framePush == PoolNoFramePush) == + (klass->framePop == PoolNoFramePop)); + CHECKL((klass->rampBegin == PoolNoRampBegin) == + (klass->rampEnd == PoolNoRampEnd)); + + CHECKS(PoolClass, klass); return TRUE; } @@ -83,20 +77,24 @@ Bool PoolClassCheck(PoolClass class) Bool PoolCheck(Pool pool) { + PoolClass klass; /* Checks ordered as per struct decl in */ CHECKS(Pool, pool); + CHECKC(AbstractPool, pool); /* Break modularity for checking efficiency */ CHECKL(pool->serial < ArenaGlobals(pool->arena)->poolSerial); - CHECKD(PoolClass, pool->class); + klass = ClassOfPoly(Pool, pool); + CHECKD(PoolClass, klass); CHECKU(Arena, pool->arena); CHECKD_NOSIG(Ring, &pool->arenaRing); CHECKD_NOSIG(Ring, &pool->bufferRing); /* Cannot check pool->bufferSerial */ CHECKD_NOSIG(Ring, &pool->segRing); CHECKL(AlignCheck(pool->alignment)); - /* 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)); + CHECKL(ShiftCheck(pool->alignShift)); + CHECKL(pool->alignment == PoolGrainsSize(pool, (Align)1)); + if (pool->format != NULL) + CHECKD(Format, pool->format); return TRUE; } @@ -120,70 +118,29 @@ ARG_DEFINE_KEY(INTERIOR, Bool); /* PoolInit -- initialize a pool * * Initialize the generic fields of the pool and calls class-specific - * init. See . */ + * init. See . + */ -Res PoolInit(Pool pool, Arena arena, PoolClass class, ArgList args) +Res PoolInit(Pool pool, Arena arena, PoolClass klass, ArgList args) { Res res; - Word classId; - Globals globals; - AVER(pool != NULL); - AVERT(Arena, arena); - AVERT(PoolClass, class); - globals = ArenaGlobals(arena); + AVERT(PoolClass, klass); - pool->class = class; - /* label the pool class with its name */ - if (!class->labelled) { - /* We could still get multiple labelling if multiple instances of */ - /* the pool class get created simultaneously, but it's not worth */ - /* putting another lock in the code. */ - class->labelled = TRUE; - classId = EventInternString(class->name); - /* NOTE: this breaks */ - EventLabelAddr((Addr)class, classId); - } - - pool->arena = arena; - RingInit(&pool->arenaRing); - RingInit(&pool->bufferRing); - RingInit(&pool->segRing); - pool->bufferSerial = (Serial)0; - pool->alignment = MPS_PF_ALIGN; - pool->format = NULL; - pool->fix = class->fix; - - /* Initialise signature last; see */ - pool->sig = PoolSig; - pool->serial = globals->poolSerial; - ++(globals->poolSerial); - - AVERT(Pool, pool); - - /* Do class-specific initialization. */ - res = (*class->init)(pool, args); + res = klass->init(pool, arena, klass, args); if (res != ResOK) - goto failInit; + return res; - /* Add initialized pool to list of pools in arena. */ - RingAppend(&globals->poolRing, &pool->arenaRing); + EVENT3(PoolInit, pool, PoolArena(pool), ClassOfPoly(Pool, pool)); return ResOK; - -failInit: - pool->sig = SigInvalid; /* Leave arena->poolSerial incremented */ - RingFinish(&pool->segRing); - RingFinish(&pool->bufferRing); - RingFinish(&pool->arenaRing); - return res; } /* PoolCreate: Allocate and initialise pool */ Res PoolCreate(Pool *poolReturn, Arena arena, - PoolClass class, ArgList args) + PoolClass klass, ArgList args) { Res res; Pool pool; @@ -191,22 +148,17 @@ Res PoolCreate(Pool *poolReturn, Arena arena, AVER(poolReturn != NULL); AVERT(Arena, arena); - AVERT(PoolClass, class); + AVERT(PoolClass, klass); /* .space.alloc: Allocate the pool instance structure with the size */ /* requested in the pool class. See .space.free */ - res = ControlAlloc(&base, arena, class->size, - /* withReservoirPermit */ FALSE); + res = ControlAlloc(&base, arena, klass->size); if (res != ResOK) goto failControlAlloc; - - /* base is the address of the class-specific pool structure. */ - /* We calculate the address of the generic pool structure within the */ - /* instance by using the offset information from the class. */ - pool = (Pool)PointerAdd(base, class->offset); + pool = (Pool)base; /* Initialize the pool. */ - res = PoolInit(pool, arena, class, args); + res = PoolInit(pool, arena, klass, args); if (res != ResOK) goto failPoolInit; @@ -214,7 +166,7 @@ Res PoolCreate(Pool *poolReturn, Arena arena, return ResOK; failPoolInit: - ControlFree(arena, base, class->size); + ControlFree(arena, base, klass->size); failControlAlloc: return res; } @@ -225,19 +177,7 @@ Res PoolCreate(Pool *poolReturn, Arena arena, void PoolFinish(Pool pool) { AVERT(Pool, pool); - - /* Do any class-specific finishing. */ - (*pool->class->finish)(pool); - - /* Detach the pool from the arena, and unsig it. */ - RingRemove(&pool->arenaRing); - pool->sig = SigInvalid; - - RingFinish(&pool->segRing); - RingFinish(&pool->bufferRing); - RingFinish(&pool->arenaRing); - - EVENT1(PoolFinish, pool); + Method(Inst, pool, finish)(MustBeA(Inst, pool)); } @@ -245,21 +185,16 @@ void PoolFinish(Pool pool) void PoolDestroy(Pool pool) { - PoolClass class; Arena arena; - Addr base; + Size size; AVERT(Pool, pool); - - class = pool->class; /* } In case PoolFinish changes these */ - arena = pool->arena; /* } */ - - /* Finish the pool instance structure. */ + arena = pool->arena; + size = ClassOfPoly(Pool, pool)->size; PoolFinish(pool); /* .space.free: Free the pool instance structure. See .space.alloc */ - base = AddrSub((Addr)pool, (Size)(class->offset)); - ControlFree(arena, base, (Size)(class->size)); + ControlFree(arena, pool, size); } @@ -268,28 +203,28 @@ void PoolDestroy(Pool pool) BufferClass PoolDefaultBufferClass(Pool pool) { AVERT(Pool, pool); - return (*pool->class->bufferClass)(); + return Method(Pool, pool, bufferClass)(); } -/* PoolAlloc -- allocate a block of memory from a pool */ +/* PoolAlloc -- allocate a block of memory from a pool + * + * .alloc.critical: In manual-allocation-bound programs this is on the + * critical path via mps_alloc. + */ -Res PoolAlloc(Addr *pReturn, Pool pool, Size size, - Bool withReservoirPermit) +Res PoolAlloc(Addr *pReturn, Pool pool, Size size) { Res res; - AVER(pReturn != NULL); - AVERT(Pool, pool); - AVER(size > 0); - AVERT(Bool, withReservoirPermit); + AVER_CRITICAL(pReturn != NULL); + AVERT_CRITICAL(Pool, pool); + AVER_CRITICAL(size > 0); - res = (*pool->class->alloc)(pReturn, pool, size, withReservoirPermit); + res = Method(Pool, pool, alloc)(pReturn, pool, size); if (res != ResOK) return res; /* Make sure that the allocated address was in the pool's memory. */ - /* .hasaddr.critical: The PoolHasAddr check is expensive, and in */ - /* allocation-bound programs this is on the critical path. */ AVER_CRITICAL(PoolHasAddr(pool, *pReturn)); /* All allocations should be aligned to the pool's alignment */ AVER_CRITICAL(AddrIsAligned(*pReturn, pool->alignment)); @@ -306,7 +241,7 @@ Res PoolAlloc(Addr *pReturn, Pool pool, Size size, /* PoolFree -- deallocate a block of memory allocated from the pool */ -void PoolFree(Pool pool, Addr old, Size size) +void (PoolFree)(Pool pool, Addr old, Size size) { AVERT(Pool, pool); AVER(old != NULL); @@ -315,186 +250,20 @@ void PoolFree(Pool pool, Addr old, Size size) AVER(AddrIsAligned(old, pool->alignment)); AVER(PoolHasRange(pool, old, AddrAdd(old, size))); - (*pool->class->free)(pool, old, size); + PoolFreeMacro(pool, old, size); EVENT3(PoolFree, pool, old, size); } -Res PoolAccess(Pool pool, Seg seg, Addr addr, - AccessSet mode, MutatorFaultContext context) -{ - AVERT(Pool, pool); - AVERT(Seg, seg); - AVER(SegBase(seg) <= addr); - AVER(addr < SegLimit(seg)); - AVERT(AccessSet, mode); - /* Can't check MutatorFaultContext as there is no check method */ +/* PoolSegPoolGen -- get pool generation for a segment */ - return (*pool->class->access)(pool, seg, addr, mode, context); -} - - -/* PoolWhiten, PoolGrey, PoolBlacken -- change color of a segment in the pool */ - -Res PoolWhiten(Pool pool, Trace trace, Seg seg) +PoolGen PoolSegPoolGen(Pool pool, Seg seg) { - AVERT(Pool, pool); - AVERT(Trace, trace); - AVERT(Seg, seg); - AVER(PoolArena(pool) == trace->arena); - AVER(SegPool(seg) == pool); - return (*pool->class->whiten)(pool, trace, seg); -} - -void PoolGrey(Pool pool, Trace trace, Seg seg) -{ - AVERT(Pool, pool); - AVERT(Trace, trace); - AVERT(Seg, seg); - AVER(pool->arena == trace->arena); - AVER(SegPool(seg) == pool); - (*pool->class->grey)(pool, trace, seg); -} - -void PoolBlacken(Pool pool, TraceSet traceSet, Seg seg) -{ - AVERT(Pool, pool); - AVERT(TraceSet, traceSet); - AVERT(Seg, seg); - AVER(SegPool(seg) == pool); - (*pool->class->blacken)(pool, traceSet, seg); -} - - -/* PoolScan -- scan a segment in the pool */ - -Res PoolScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg) -{ - AVER(totalReturn != NULL); - AVERT(ScanState, ss); - AVERT(Pool, pool); - AVERT(Seg, seg); - AVER(ss->arena == pool->arena); - - /* The segment must belong to the pool. */ - AVER(pool == SegPool(seg)); - - /* We check that either ss->rank is in the segment's - * ranks, or that ss->rank is exact. The check is more complicated if - * we actually have multiple ranks in a seg. - * See */ - AVER(ss->rank == RankEXACT || RankSetIsMember(SegRankSet(seg), ss->rank)); - - /* Should only scan segments which contain grey objects. */ - AVER(TraceSetInter(SegGrey(seg), ss->traces) != TraceSetEMPTY); - - return (*pool->class->scan)(totalReturn, ss, pool, seg); -} - - -/* PoolFix* -- fix a reference to an object in this pool - * - * See for macro version; see . - */ - -Res (PoolFix)(Pool pool, ScanState ss, Seg seg, Addr *refIO) -{ - 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_CRITICAL(TraceSetInter(SegWhite(seg), ss->traces) != TraceSetEMPTY); - - return PoolFix(pool, ss, seg, refIO); -} - -Res PoolFixEmergency(Pool pool, ScanState ss, Seg seg, Addr *refIO) -{ - Res res; - - 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_CRITICAL(TraceSetInter(SegWhite(seg), ss->traces) != TraceSetEMPTY); - - res = (pool->class->fixEmergency)(pool, ss, seg, refIO); - AVER_CRITICAL(res == ResOK); - return res; -} - - -/* PoolReclaim -- reclaim a segment in the pool */ - -void PoolReclaim(Pool pool, Trace trace, Seg seg) -{ - AVERT_CRITICAL(Pool, pool); - AVERT_CRITICAL(Trace, trace); - AVERT_CRITICAL(Seg, seg); - AVER_CRITICAL(pool->arena == trace->arena); - AVER_CRITICAL(SegPool(seg) == pool); - - /* There shouldn't be any grey things left for this trace. */ - AVER_CRITICAL(!TraceSetIsMember(SegGrey(seg), trace)); - /* Should only be reclaiming segments which are still white. */ - AVER_CRITICAL(TraceSetIsMember(SegWhite(seg), trace)); - - (*pool->class->reclaim)(pool, trace, seg); -} - - -/* PoolTraceEnd -- do end-of-trace work - * - * This method is for a pool class to do final end-of-trace work, - * after all reclaiming is complete. For example, emitting - * diagnostics about what happened during the trace. - */ - -void PoolTraceEnd(Pool pool, Trace trace) -{ - AVERT(Pool, pool); - AVERT(Trace, trace); - AVER(pool->arena == trace->arena); - - (*pool->class->traceEnd)(pool, trace); -} - - -/* PoolAddrObject -- find client pointer to object containing addr - * See user documentation for mps_addr_object. - * addr is known to belong to seg, which belongs to pool. - * See job003589. - */ - -Res PoolAddrObject(Addr *pReturn, Pool pool, Seg seg, Addr addr) -{ - AVER(pReturn != NULL); AVERT(Pool, pool); AVERT(Seg, seg); AVER(pool == SegPool(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, FormattedObjectsVisitor f, void *p, size_t s) -{ - AVERT(Pool, pool); - AVERT(Seg, seg); - AVER(FUNCHECK(f)); - /* p and s are arbitrary values, hence can't be checked. */ - - (*pool->class->walk)(pool, seg, f, p, s); + return Method(Pool, pool, segPoolGen)(pool, seg); } @@ -509,7 +278,7 @@ void PoolFreeWalk(Pool pool, FreeBlockVisitor f, void *p) AVER(FUNCHECK(f)); /* p is arbitrary, hence can't be checked. */ - (*pool->class->freewalk)(pool, f, p); + Method(Pool, pool, freewalk)(pool, f, p); } @@ -519,7 +288,7 @@ Size PoolTotalSize(Pool pool) { AVERT(Pool, pool); - return (*pool->class->totalSize)(pool); + return Method(Pool, pool, totalSize)(pool); } @@ -529,7 +298,7 @@ Size PoolFreeSize(Pool pool) { AVERT(Pool, pool); - return (*pool->class->freeSize)(pool); + return Method(Pool, pool, freeSize)(pool); } @@ -537,58 +306,18 @@ Size PoolFreeSize(Pool pool) 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; - - res = WriteF(stream, depth, - "Pool $P ($U) {\n", (WriteFP)pool, (WriteFU)pool->serial, - " class $P (\"$S\")\n", - (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 (NULL != pool->format) { - res = FormatDescribe(pool->format, stream, depth + 2); - 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, depth + 2); - if (res != ResOK) - return res; - } - - res = WriteF(stream, depth, - "} Pool $P ($U)\n", (WriteFP)pool, (WriteFU)pool->serial, - NULL); - if (res != ResOK) - return res; - - return ResOK; + return Method(Inst, pool, describe)(MustBeA(Inst, pool), stream, depth); } -/* PoolFormat +/* PoolFormat -- get the format of a pool, if any * * Returns the format of the pool (the format of objects in the pool). * If the pool is unformatted or doesn't declare a format then this * function returns FALSE and does not update *formatReturn. Otherwise * this function returns TRUE and *formatReturn is updated to be the - * pool's format. */ + * pool's format. + */ Bool PoolFormat(Format *formatReturn, Pool pool) { @@ -683,8 +412,8 @@ Bool PoolHasRange(Pool pool, Addr base, Addr limit) Arena arena; Bool managed; - AVERT(Pool, pool); - AVER(base < limit); + AVERT_CRITICAL(Pool, pool); + AVER_CRITICAL(base < limit); arena = PoolArena(pool); managed = PoolOfRange(&rangePool, arena, base, limit); @@ -694,7 +423,7 @@ Bool PoolHasRange(Pool pool, Addr base, Addr limit) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2015 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/poolabs.c b/mps/code/poolabs.c index 30f5d0f999e..8920baa694e 100644 --- a/mps/code/poolabs.c +++ b/mps/code/poolabs.c @@ -1,7 +1,7 @@ /* poolabs.c: ABSTRACT POOL CLASSES * * $Id$ - * Copyright (c) 2001-2015 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. * * PURPOSE @@ -20,8 +20,7 @@ * AbstractPoolClass - implements init, finish, describe * AbstractBufferPoolClass - implements the buffer protocol * AbstractSegBufPoolClass - uses SegBuf buffer class - * AbstractScanPoolClass - implements basic scanning - * AbstractCollectPoolClass - implements basic GC + * AbstractCollectPoolClass - implements basic GC */ #include "mpm.h" @@ -29,13 +28,6 @@ SRCID(poolabs, "$Id$"); -typedef PoolClassStruct AbstractPoolClassStruct; -typedef PoolClassStruct AbstractBufferPoolClassStruct; -typedef PoolClassStruct AbstractSegBufPoolClassStruct; -typedef PoolClassStruct AbstractScanPoolClassStruct; -typedef PoolClassStruct AbstractCollectPoolClassStruct; - - /* Mixins: * * For now (at least) we're avoiding multiple inheritance. @@ -49,130 +41,162 @@ typedef PoolClassStruct AbstractCollectPoolClassStruct; /* PoolClassMixInBuffer -- mix in the protocol for buffer reserve / commit */ -void PoolClassMixInBuffer(PoolClass class) +void PoolClassMixInBuffer(PoolClass klass) { - /* Can't check class because it's not initialized yet */ - class->bufferFill = PoolTrivBufferFill; - class->bufferEmpty = PoolTrivBufferEmpty; + /* Can't check klass because it's not initialized yet */ + klass->bufferFill = PoolTrivBufferFill; + klass->bufferEmpty = PoolTrivBufferEmpty; /* By default, buffered pools treat frame operations as NOOPs */ - class->framePush = PoolTrivFramePush; - class->framePop = PoolTrivFramePop; - class->framePopPending = PoolTrivFramePopPending; - class->bufferClass = BufferClassGet; -} - - -/* PoolClassMixInScan -- mix in the protocol for scanning */ - -void PoolClassMixInScan(PoolClass class) -{ - /* Can't check class because it's not initialized yet */ - class->access = PoolSegAccess; - class->blacken = PoolTrivBlacken; - class->grey = PoolTrivGrey; - /* scan is part of the scanning protocol, but there is no useful - * default method. - */ - class->scan = NULL; -} - - -/* PoolClassMixInFormat -- mix in the protocol for formatted pools */ - -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; + klass->framePush = PoolTrivFramePush; + klass->framePop = PoolTrivFramePop; + klass->bufferClass = BufferClassGet; } /* PoolClassMixInCollect -- mix in the protocol for GC */ -void PoolClassMixInCollect(PoolClass class) +void PoolClassMixInCollect(PoolClass klass) { - /* Can't check class because it's not initialized yet */ - class->attr |= AttrGC; - class->whiten = PoolTrivWhiten; - /* 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; + /* Can't check klass because it's not initialized yet */ + klass->attr |= AttrGC; + klass->rampBegin = PoolTrivRampBegin; + klass->rampEnd = PoolTrivRampEnd; } /* Classes */ -DEFINE_CLASS(AbstractPoolClass, class) +/* PoolAbsInit -- initialize an abstract pool instance */ + +Res PoolAbsInit(Pool pool, Arena arena, PoolClass klass, ArgList args) { - INHERIT_CLASS(&class->protocol, ProtocolClass); - class->name = "ABSTRACT"; - class->size = 0; - class->offset = 0; - class->attr = 0; - class->varargs = ArgTrivVarargs; - class->init = PoolTrivInit; - class->finish = PoolTrivFinish; - class->alloc = PoolNoAlloc; - class->free = PoolNoFree; - class->bufferFill = PoolNoBufferFill; - class->bufferEmpty = PoolNoBufferEmpty; - class->access = PoolNoAccess; - class->whiten = PoolNoWhiten; - class->grey = PoolNoGrey; - class->blacken = PoolNoBlacken; - class->scan = PoolNoScan; - class->fix = PoolNoFix; - class->fixEmergency = PoolNoFix; - class->reclaim = PoolNoReclaim; - class->traceEnd = PoolTrivTraceEnd; - class->rampBegin = PoolNoRampBegin; - class->rampEnd = PoolNoRampEnd; - class->framePush = PoolNoFramePush; - class->framePop = PoolNoFramePop; - class->framePopPending = PoolNoFramePopPending; - class->addrObject = PoolNoAddrObject; - class->walk = PoolNoWalk; - class->freewalk = PoolTrivFreeWalk; - class->bufferClass = PoolNoBufferClass; - class->describe = PoolTrivDescribe; - class->debugMixin = PoolNoDebugMixin; - class->totalSize = PoolNoSize; - class->freeSize = PoolNoSize; - class->labelled = FALSE; - class->sig = PoolClassSig; + ArgStruct arg; + + AVER(pool != NULL); + AVERT(Arena, arena); + UNUSED(args); + UNUSED(klass); /* used for debug pools only */ + + /* Superclass init */ + InstInit(CouldBeA(Inst, pool)); + + pool->arena = arena; + RingInit(&pool->arenaRing); + RingInit(&pool->bufferRing); + RingInit(&pool->segRing); + pool->bufferSerial = (Serial)0; + pool->alignment = MPS_PF_ALIGN; + pool->alignShift = SizeLog2(pool->alignment); + pool->format = NULL; + + if (ArgPick(&arg, args, MPS_KEY_FORMAT)) { + Format format = arg.val.format; + AVERT(Format, format); + AVER(FormatArena(format) == arena); + pool->format = format; + /* .init.format: Increment reference count on the format for + consistency checking. See .finish.format. */ + ++pool->format->poolCount; + } else { + pool->format = NULL; + } + + pool->serial = ArenaGlobals(arena)->poolSerial; + ++ArenaGlobals(arena)->poolSerial; + + /* Initialise signature last; see */ + SetClassOfPoly(pool, CLASS(AbstractPool)); + pool->sig = PoolSig; + AVERT(Pool, pool); + + /* Add initialized pool to list of pools in arena. */ + RingAppend(ArenaPoolRing(arena), PoolArenaRing(pool)); + + return ResOK; } -DEFINE_CLASS(AbstractBufferPoolClass, class) + +/* PoolAbsFinish -- finish an abstract pool instance */ + +void PoolAbsFinish(Inst inst) { - INHERIT_CLASS(class, AbstractPoolClass); - PoolClassMixInBuffer(class); + Pool pool = MustBeA(AbstractPool, inst); + + /* Detach the pool from the arena and format, and unsig it. */ + RingRemove(PoolArenaRing(pool)); + + /* .finish.format: Decrement the reference count on the format for + consistency checking. See .format.init. */ + if (pool->format) { + AVER(pool->format->poolCount > 0); + --pool->format->poolCount; + pool->format = NULL; + } + + pool->sig = SigInvalid; + InstFinish(CouldBeA(Inst, pool)); + + RingFinish(&pool->segRing); + RingFinish(&pool->bufferRing); + RingFinish(&pool->arenaRing); + + EVENT1(PoolFinish, pool); } -DEFINE_CLASS(AbstractSegBufPoolClass, class) +DEFINE_CLASS(Inst, PoolClass, klass) { - INHERIT_CLASS(class, AbstractBufferPoolClass); - class->bufferClass = SegBufClassGet; + INHERIT_CLASS(klass, PoolClass, InstClass); + AVERT(InstClass, klass); } -DEFINE_CLASS(AbstractScanPoolClass, class) +DEFINE_CLASS(Pool, AbstractPool, klass) { - INHERIT_CLASS(class, AbstractSegBufPoolClass); - PoolClassMixInScan(class); + INHERIT_CLASS(&klass->instClassStruct, AbstractPool, Inst); + klass->instClassStruct.describe = PoolAbsDescribe; + klass->instClassStruct.finish = PoolAbsFinish; + klass->size = sizeof(PoolStruct); + klass->attr = 0; + klass->varargs = ArgTrivVarargs; + klass->init = PoolAbsInit; + klass->alloc = PoolNoAlloc; + klass->free = PoolNoFree; + klass->bufferFill = PoolNoBufferFill; + klass->bufferEmpty = PoolNoBufferEmpty; + klass->rampBegin = PoolNoRampBegin; + klass->rampEnd = PoolNoRampEnd; + klass->framePush = PoolNoFramePush; + klass->framePop = PoolNoFramePop; + klass->segPoolGen = PoolNoSegPoolGen; + klass->freewalk = PoolTrivFreeWalk; + klass->bufferClass = PoolNoBufferClass; + klass->debugMixin = PoolNoDebugMixin; + klass->totalSize = PoolNoSize; + klass->freeSize = PoolNoSize; + klass->sig = PoolClassSig; + AVERT(PoolClass, klass); } -DEFINE_CLASS(AbstractCollectPoolClass, class) +DEFINE_CLASS(Pool, AbstractBufferPool, klass) { - INHERIT_CLASS(class, AbstractScanPoolClass); - PoolClassMixInCollect(class); + INHERIT_CLASS(klass, AbstractBufferPool, AbstractPool); + PoolClassMixInBuffer(klass); + AVERT(PoolClass, klass); +} + +DEFINE_CLASS(Pool, AbstractSegBufPool, klass) +{ + INHERIT_CLASS(klass, AbstractSegBufPool, AbstractBufferPool); + klass->bufferClass = SegBufClassGet; + klass->bufferEmpty = PoolSegBufferEmpty; + AVERT(PoolClass, klass); +} + +DEFINE_CLASS(Pool, AbstractCollectPool, klass) +{ + INHERIT_CLASS(klass, AbstractCollectPool, AbstractSegBufPool); + PoolClassMixInCollect(klass); + AVERT(PoolClass, klass); } @@ -181,39 +205,20 @@ DEFINE_CLASS(AbstractCollectPoolClass, class) * See and */ - -void PoolTrivFinish(Pool pool) -{ - AVERT(Pool, pool); - NOOP; -} - -Res PoolTrivInit(Pool pool, ArgList args) -{ - AVERT(Pool, pool); - AVERT(ArgList, args); - UNUSED(args); - return ResOK; -} - -Res PoolNoAlloc(Addr *pReturn, Pool pool, Size size, - Bool withReservoirPermit) +Res PoolNoAlloc(Addr *pReturn, Pool pool, Size size) { AVER(pReturn != NULL); AVERT(Pool, pool); AVER(size > 0); - AVERT(Bool, withReservoirPermit); NOTREACHED; return ResUNIMPL; } -Res PoolTrivAlloc(Addr *pReturn, Pool pool, Size size, - Bool withReservoirPermit) +Res PoolTrivAlloc(Addr *pReturn, Pool pool, Size size) { AVER(pReturn != NULL); AVERT(Pool, pool); AVER(size > 0); - AVERT(Bool, withReservoirPermit); return ResLIMIT; } @@ -233,24 +238,29 @@ void PoolTrivFree(Pool pool, Addr old, Size size) NOOP; /* trivial free has no effect */ } +PoolGen PoolNoSegPoolGen(Pool pool, Seg seg) +{ + AVERT(Pool, pool); + AVERT(Seg, seg); + AVER(pool == SegPool(seg)); + NOTREACHED; + return NULL; +} Res PoolNoBufferFill(Addr *baseReturn, Addr *limitReturn, - Pool pool, Buffer buffer, Size size, - Bool withReservoirPermit) + Pool pool, Buffer buffer, Size size) { AVER(baseReturn != NULL); AVER(limitReturn != NULL); AVERT(Pool, pool); AVERT(Buffer, buffer); AVER(size > 0); - AVERT(Bool, withReservoirPermit); NOTREACHED; return ResUNIMPL; } Res PoolTrivBufferFill(Addr *baseReturn, Addr *limitReturn, - Pool pool, Buffer buffer, Size size, - Bool withReservoirPermit) + Pool pool, Buffer buffer, Size size) { Res res; Addr p; @@ -260,9 +270,8 @@ Res PoolTrivBufferFill(Addr *baseReturn, Addr *limitReturn, AVERT(Pool, pool); AVERT(Buffer, buffer); AVER(size > 0); - AVERT(Bool, withReservoirPermit); - res = PoolAlloc(&p, pool, size, withReservoirPermit); + res = PoolAlloc(&p, pool, size); if (res != ResOK) return res; @@ -272,34 +281,82 @@ Res PoolTrivBufferFill(Addr *baseReturn, Addr *limitReturn, } -void PoolNoBufferEmpty(Pool pool, Buffer buffer, - Addr init, Addr limit) +void PoolNoBufferEmpty(Pool pool, Buffer buffer) { AVERT(Pool, pool); AVERT(Buffer, buffer); AVER(BufferIsReady(buffer)); - AVER(init <= limit); NOTREACHED; } -void PoolTrivBufferEmpty(Pool pool, Buffer buffer, Addr init, Addr limit) +void PoolTrivBufferEmpty(Pool pool, Buffer buffer) { + Addr init, limit; + AVERT(Pool, pool); AVERT(Buffer, buffer); AVER(BufferIsReady(buffer)); + + init = BufferGetInit(buffer); + limit = BufferLimit(buffer); AVER(init <= limit); if (limit > init) PoolFree(pool, init, AddrOffset(init, limit)); } - -Res PoolTrivDescribe(Pool pool, mps_lib_FILE *stream, Count depth) +void PoolSegBufferEmpty(Pool pool, Buffer buffer) { + Seg seg; + AVERT(Pool, pool); - AVER(stream != NULL); - return WriteF(stream, depth, - "No class-specific description available.\n", - NULL); + AVERT(Buffer, buffer); + AVER(BufferIsReady(buffer)); + seg = BufferSeg(buffer); + AVERT(Seg, seg); + + Method(Seg, seg, bufferEmpty)(seg, buffer); +} + + +Res PoolAbsDescribe(Inst inst, mps_lib_FILE *stream, Count depth) +{ + Pool pool = CouldBeA(AbstractPool, inst); + Res res; + Ring node, nextNode; + + if (!TESTC(AbstractPool, pool)) + return ResPARAM; + if (stream == NULL) + return ResPARAM; + + res = InstDescribe(CouldBeA(Inst, pool), stream, depth); + if (res != ResOK) + return res; + + res = WriteF(stream, depth + 2, + "serial $U\n", (WriteFU)pool->serial, + "arena $P ($U)\n", + (WriteFP)pool->arena, (WriteFU)pool->arena->serial, + "alignment $W\n", (WriteFW)pool->alignment, + "alignShift $W\n", (WriteFW)pool->alignShift, + NULL); + if (res != ResOK) + return res; + + if (pool->format != NULL) { + res = FormatDescribe(pool->format, 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, depth + 2); + if (res != ResOK) + return res; + } + + return ResOK; } @@ -320,231 +377,6 @@ Res PoolTrivTraceBegin(Pool pool, Trace trace) return ResOK; } -/* NoAccess - * - * Should be used (for the access method) by Pool Classes which do - * not expect to ever have pages which the mutator will fault on. - * That is, no protected pages, or only pages which are inaccessible - * by the mutator are protected. - */ -Res PoolNoAccess(Pool pool, Seg seg, Addr addr, - AccessSet mode, MutatorFaultContext context) -{ - AVERT(Pool, pool); - AVERT(Seg, seg); - AVER(SegBase(seg) <= addr); - AVER(addr < SegLimit(seg)); - AVERT(AccessSet, mode); - /* can't check context as there is no Check method */ - UNUSED(mode); - UNUSED(context); - - NOTREACHED; - return ResUNIMPL; -} - - -/* SegAccess - * - * See also PoolSingleAccess - * - * Should be used (for the access method) by Pool Classes which intend - * to handle page faults by scanning the entire segment and lowering - * the barrier. - */ -Res PoolSegAccess(Pool pool, Seg seg, Addr addr, - AccessSet mode, MutatorFaultContext context) -{ - AVERT(Pool, pool); - AVERT(Seg, seg); - AVER(SegBase(seg) <= addr); - AVER(addr < SegLimit(seg)); - AVER(SegPool(seg) == pool); - AVERT(AccessSet, mode); - /* can't check context as there is no Check method */ - - UNUSED(addr); - UNUSED(context); - TraceSegAccess(PoolArena(pool), seg, mode); - return ResOK; -} - - -/* SingleAccess - * - * See also ArenaRead, and PoolSegAccess. - * - * Handles page faults by attempting emulation. If the faulting - * instruction cannot be emulated then this function returns ResFAIL. - * - * Due to the assumptions made below, pool classes should only use - * this function if all words in an object are tagged or traceable. - * - * .single-access.assume.ref: It currently assumes that the address - * being faulted on contains a plain reference or a tagged non-reference. - * .single-access.improve.format: Later this will be abstracted - * through the cleint object format interface, so that - * no such assumption is necessary. - */ -Res PoolSingleAccess(Pool pool, Seg seg, Addr addr, - AccessSet mode, MutatorFaultContext context) -{ - Arena arena; - - AVERT(Pool, pool); - AVERT(Seg, seg); - AVER(SegBase(seg) <= addr); - AVER(addr < SegLimit(seg)); - AVER(SegPool(seg) == pool); - AVERT(AccessSet, mode); - /* can't check context as there is no Check method */ - - arena = PoolArena(pool); - - if(ProtCanStepInstruction(context)) { - Ref ref; - Res res; - - ShieldExpose(arena, seg); - - if(mode & SegSM(seg) & AccessREAD) { - /* Read access. */ - /* .single-access.assume.ref */ - /* .single-access.improve.format */ - ref = *(Ref *)addr; - /* .tagging: Check that the reference is aligned to a word boundary */ - /* (we assume it is not a reference otherwise). */ - if(WordIsAligned((Word)ref, sizeof(Word))) { - Rank rank; - /* See the note in TraceRankForAccess */ - /* (). */ - - rank = TraceRankForAccess(arena, seg); - TraceScanSingleRef(arena->flippedTraces, rank, arena, - seg, (Ref *)addr); - } - } - res = ProtStepInstruction(context); - AVER(res == ResOK); - - /* Update SegSummary according to the possibly changed reference. */ - ref = *(Ref *)addr; - /* .tagging: ought to check the reference for a tag. But - * this is conservative. */ - SegSetSummary(seg, RefSetAdd(arena, SegSummary(seg), ref)); - - ShieldCover(arena, seg); - - return ResOK; - } else { - /* couldn't single-step instruction */ - return ResFAIL; - } -} - - -Res PoolTrivWhiten(Pool pool, Trace trace, Seg seg) -{ - AVERT(Pool, pool); - AVERT(Trace, trace); - AVERT(Seg, seg); - - SegSetWhite(seg, TraceSetAdd(SegWhite(seg), trace)); - - return ResOK; -} - -Res PoolNoWhiten(Pool pool, Trace trace, Seg seg) -{ - AVERT(Pool, pool); - AVERT(Trace, trace); - AVERT(Seg, seg); - NOTREACHED; - return ResUNIMPL; -} - - -void PoolNoGrey(Pool pool, Trace trace, Seg seg) -{ - AVERT(Pool, pool); - AVERT(Trace, trace); - AVERT(Seg, seg); - NOTREACHED; -} - -void PoolTrivGrey(Pool pool, Trace trace, Seg seg) -{ - AVERT(Pool, pool); - AVERT(Trace, trace); - AVERT(Seg, seg); - - /* If we had a (partially) white seg, then other parts of the */ - /* same seg might need to get greyed. In fact, all current pools */ - /* only ever Whiten a whole seg, so we never need to Greyen any */ - /* part of an already Whitened seg. So we hereby exclude white */ - /* segs. */ - /* @@@@ This should not really be called 'trivial'! */ - if(!TraceSetIsMember(SegWhite(seg), trace)) - SegSetGrey(seg, TraceSetSingle(trace)); -} - - -void PoolNoBlacken(Pool pool, TraceSet traceSet, Seg seg) -{ - AVERT(Pool, pool); - AVERT(TraceSet, traceSet); - AVERT(Seg, seg); - NOTREACHED; -} - -void PoolTrivBlacken(Pool pool, TraceSet traceSet, Seg seg) -{ - AVERT(Pool, pool); - AVERT(TraceSet, traceSet); - AVERT(Seg, seg); - - /* The trivial blacken method does nothing; for pool classes which do */ - /* not keep additional colour information. */ - NOOP; -} - - -Res PoolNoScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg) -{ - AVER(totalReturn != NULL); - AVERT(ScanState, ss); - AVERT(Pool, pool); - AVERT(Seg, seg); - NOTREACHED; - return ResUNIMPL; -} - -Res PoolNoFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) -{ - AVERT(Pool, pool); - AVERT(ScanState, ss); - AVERT(Seg, seg); - AVER(refIO != NULL); - NOTREACHED; - return ResUNIMPL; -} - -void PoolNoReclaim(Pool pool, Trace trace, Seg seg) -{ - AVERT(Pool, pool); - AVERT(Trace, trace); - AVERT(Seg, seg); - NOTREACHED; -} - -void PoolTrivTraceEnd(Pool pool, Trace trace) -{ - AVERT(Pool, pool); - AVERT(Trace, trace); - NOOP; -} - - void PoolNoRampBegin(Pool pool, Buffer buf, Bool collectAll) { AVERT(Pool, pool); @@ -598,16 +430,6 @@ Res PoolNoFramePop(Pool pool, Buffer buf, AllocFrame frame) } -void PoolNoFramePopPending(Pool pool, Buffer buf, AllocFrame frame) -{ - AVERT(Pool, pool); - AVERT(Buffer, buf); - /* frame is of an abstract type & can't be checked */ - UNUSED(frame); - NOTREACHED; -} - - Res PoolTrivFramePush(AllocFrame *frameReturn, Pool pool, Buffer buf) { AVER(frameReturn != NULL); @@ -627,41 +449,6 @@ Res PoolTrivFramePop(Pool pool, Buffer buf, AllocFrame frame) } -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); - AVER(addr < SegLimit(seg)); - return ResUNIMPL; -} - -void PoolNoWalk(Pool pool, Seg seg, FormattedObjectsVisitor f, - void *p, size_t s) -{ - AVERT(Pool, pool); - AVERT(Seg, seg); - AVER(FUNCHECK(f)); - /* p and s are arbitrary, hence can't be checked */ - UNUSED(p); - UNUSED(s); - - NOTREACHED; -} - - void PoolTrivFreeWalk(Pool pool, FreeBlockVisitor f, void *p) { AVERT(Pool, pool); @@ -691,7 +478,7 @@ Size PoolNoSize(Pool pool) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2015 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/poolamc.c b/mps/code/poolamc.c index 7b625c4dac4..ef4578d2e13 100644 --- a/mps/code/poolamc.c +++ b/mps/code/poolamc.c @@ -1,7 +1,7 @@ /* poolamc.c: AUTOMATIC MOSTLY-COPYING MEMORY POOL CLASS * * $Id$ - * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. * * .sources: . @@ -15,25 +15,37 @@ SRCID(poolamc, "$Id$"); -/* 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 void amcSegBufferEmpty(Seg seg, Buffer buffer); +static Res amcSegWhiten(Seg seg, Trace trace); +static Res amcSegScan(Bool *totalReturn, Seg seg, ScanState ss); +static void amcSegReclaim(Seg seg, Trace trace); 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); -extern PoolClass AMCZPoolClassGet(void); -extern BufferClass amcBufClassGet(void); -extern SegClass amcSegClassGet(void); +static Res amcSegFix(Seg seg, ScanState ss, Ref *refIO); +static Res amcSegFixEmergency(Seg seg, ScanState ss, Ref *refIO); +static void amcSegWalk(Seg seg, Format format, FormattedObjectsVisitor f, + void *p, size_t s); + +/* local class declations */ + +typedef AMC AMCZPool; +#define AMCZPoolCheck AMCCheck +DECLARE_CLASS(Pool, AMCZPool, AbstractCollectPool); + +typedef AMC AMCPool; +DECLARE_CLASS(Pool, AMCPool, AMCZPool); + +DECLARE_CLASS(Buffer, amcBuf, SegBuf); +DECLARE_CLASS(Seg, amcSeg, MutatorSeg); /* amcGenStruct -- pool AMC generation descriptor */ @@ -47,7 +59,7 @@ typedef struct amcGenStruct { Sig sig; /* */ } amcGenStruct; -#define amcGenAMC(amcgen) PoolAMC((amcgen)->pgen.pool) +#define amcGenAMC(amcgen) MustBeA(AMCZPool, (amcgen)->pgen.pool) #define amcGenPool(amcgen) ((amcgen)->pgen.pool) #define amcGenNr(amcgen) ((amcgen)->pgen.nr) @@ -70,9 +82,13 @@ enum { /* amcSegStruct -- AMC-specific fields appended to GCSegStruct * - * .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 + * .seg.accounted-as-buffered: The "accountedAsBuffered" flag is TRUE + * if the segment has an atached buffer and is accounted against the + * pool generation's bufferedSize. But note that if this is FALSE, the + * segment might still have an attached buffer -- this happens if the + * segment was condemned while the buffer was attached. + * + * .seg.old: The "old" flag is TRUE if the segment has been collected * at least once, and so its size is accounted against the pool * generation's oldSize. * @@ -93,14 +109,13 @@ typedef struct amcSegStruct { GCSegStruct gcSegStruct; /* superclass fields must come first */ amcGen gen; /* generation this segment belongs to */ Nailboard board; /* nailboard for this segment or NULL if none */ + Size forwarded[TraceLIMIT]; /* size of objects forwarded for each trace */ + BOOLFIELD(accountedAsBuffered); /* .seg.accounted-as-buffered */ BOOLFIELD(old); /* .seg.old */ BOOLFIELD(deferred); /* .seg.deferred */ Sig sig; /* */ } amcSegStruct; -#define Seg2amcSeg(seg) ((amcSeg)(seg)) -#define amcSeg2Seg(amcseg) ((Seg)(amcseg)) - ATTRIBUTE_UNUSED static Bool amcSegCheck(amcSeg amcseg) @@ -110,8 +125,9 @@ static Bool amcSegCheck(amcSeg amcseg) CHECKU(amcGen, amcseg->gen); if (amcseg->board) { CHECKD(Nailboard, amcseg->board); - CHECKL(SegNailed(amcSeg2Seg(amcseg)) != TraceSetEMPTY); + CHECKL(SegNailed(MustBeA(Seg, amcseg)) != TraceSetEMPTY); } + /* CHECKL(BoolCheck(amcseg->accountedAsBuffered)); */ /* CHECKL(BoolCheck(amcseg->old)); */ /* CHECKL(BoolCheck(amcseg->deferred)); */ return TRUE; @@ -123,11 +139,9 @@ static Bool amcSegCheck(amcSeg amcseg) 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) +static Res AMCSegInit(Seg seg, Pool pool, Addr base, Size size, ArgList args) { amcGen amcgen; - SegClass super; amcSeg amcseg; Res res; ArgStruct arg; @@ -135,28 +149,40 @@ static Res AMCSegInit(Seg seg, Pool pool, Addr base, Size size, ArgRequire(&arg, args, amcKeySegGen); amcgen = arg.val.p; - AVERT(Seg, seg); - amcseg = Seg2amcSeg(seg); - /* no useful checks for base and size */ - AVERT(Bool, reservoirPermit); - /* Initialize the superclass fields first via next-method call */ - super = SEG_SUPERCLASS(amcSegClass); - res = super->init(seg, pool, base, size, reservoirPermit, args); + res = NextMethod(Seg, amcSeg, init)(seg, pool, base, size, args); if(res != ResOK) return res; + amcseg = CouldBeA(amcSeg, seg); amcseg->gen = amcgen; amcseg->board = NULL; + amcseg->accountedAsBuffered = FALSE; amcseg->old = FALSE; amcseg->deferred = FALSE; + + SetClassOfPoly(seg, CLASS(amcSeg)); amcseg->sig = amcSegSig; - AVERT(amcSeg, amcseg); + AVERC(amcSeg, amcseg); return ResOK; } +/* amcSegFinish -- finish an AMC segment */ + +static void amcSegFinish(Inst inst) +{ + Seg seg = MustBeA(Seg, inst); + amcSeg amcseg = MustBeA(amcSeg, seg); + + amcseg->sig = SigInvalid; + + /* finish the superclass fields last */ + NextMethod(Inst, amcSeg, finish)(inst); +} + + /* AMCSegSketch -- summarise the segment state for a human reader * * Write a short human-readable text representation of the segment @@ -168,14 +194,10 @@ static Res AMCSegInit(Seg seg, Pool pool, Addr base, Size size, static void AMCSegSketch(Seg seg, char *pbSketch, size_t cbSketch) { - amcSeg amcseg; Buffer buffer; AVER(pbSketch); AVER(cbSketch >= 5); - AVERT(Seg, seg); - amcseg = Seg2amcSeg(seg); - AVERT(amcSeg, amcseg); if(SegNailed(seg) == TraceSetEMPTY) { pbSketch[0] = 'm'; /* mobile */ @@ -197,8 +219,7 @@ static void AMCSegSketch(Seg seg, char *pbSketch, size_t cbSketch) pbSketch[2] = 'W'; /* White */ } - buffer = SegBuffer(seg); - if(buffer == NULL) { + if (!SegBuffer(&buffer, seg)) { pbSketch[3] = '_'; } else { Bool mut = BufferIsMutator(buffer); @@ -234,29 +255,26 @@ static void AMCSegSketch(Seg seg, char *pbSketch, size_t cbSketch) * * See . */ -static Res AMCSegDescribe(Seg seg, mps_lib_FILE *stream, Count depth) +static Res AMCSegDescribe(Inst inst, mps_lib_FILE *stream, Count depth) { + amcSeg amcseg = CouldBeA(amcSeg, inst); + Seg seg = CouldBeA(Seg, amcseg); Res res; Pool pool; - amcSeg amcseg; - SegClass super; Addr i, p, base, limit, init; Align step; Size row; char abzSketch[5]; + Buffer buffer; - if(!TESTT(Seg, seg)) - return ResFAIL; - if(stream == NULL) - return ResFAIL; - amcseg = Seg2amcSeg(seg); - if(!TESTT(amcSeg, amcseg)) - return ResFAIL; + if (!TESTC(amcSeg, amcseg)) + return ResPARAM; + if (stream == NULL) + return ResPARAM; /* Describe the superclass fields first via next-method call */ - super = SEG_SUPERCLASS(amcSegClass); - res = super->describe(seg, stream, depth); - if(res != ResOK) + res = NextMethod(Inst, amcSeg, describe)(inst, stream, depth); + if (res != ResOK) return res; pool = SegPool(seg); @@ -267,16 +285,9 @@ static Res AMCSegDescribe(Seg seg, mps_lib_FILE *stream, Count depth) p = AddrAdd(base, pool->format->headerSize); limit = SegLimit(seg); - res = WriteF(stream, depth, - "AMC seg $P [$A,$A){\n", - (WriteFP)seg, (WriteFA)base, (WriteFA)limit, - NULL); - if(res != ResOK) - return res; - - if(amcSegHasNailboard(seg)) { + if (amcSegHasNailboard(seg)) { res = WriteF(stream, depth + 2, "Boarded\n", NULL); - } else if(SegNailed(seg) == TraceSetEMPTY) { + } else if (SegNailed(seg) == TraceSetEMPTY) { res = WriteF(stream, depth + 2, "Mobile\n", NULL); } else { res = WriteF(stream, depth + 2, "Stuck\n", NULL); @@ -286,32 +297,32 @@ static Res AMCSegDescribe(Seg seg, mps_lib_FILE *stream, Count depth) res = WriteF(stream, depth + 2, "Map: *===:object @+++:nails bbbb:buffer\n", NULL); - if(res != ResOK) + if (res != ResOK) return res; - if(SegBuffer(seg) != NULL) - init = BufferGetInit(SegBuffer(seg)); + if (SegBuffer(&buffer, seg)) + init = BufferGetInit(buffer); else init = limit; - for(i = base; i < limit; i = AddrAdd(i, row)) { + for (i = base; i < limit; i = AddrAdd(i, row)) { Addr j; char c; res = WriteF(stream, depth + 2, "$A ", (WriteFA)i, NULL); - if(res != ResOK) + if (res != ResOK) return res; /* @@@@ This misses a header-sized pad at the end. */ - for(j = i; j < AddrAdd(i, row); j = AddrAdd(j, step)) { - if(j >= limit) + 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) + else if (j >= init) c = 'b'; else { Bool nailed = amcSegHasNailboard(seg) && NailboardGet(amcSegNailboard(seg), j); - if(j == p) { + if (j == p) { c = (nailed ? '@' : '*'); p = (pool->format->skip)(p); } else { @@ -319,12 +330,12 @@ static Res AMCSegDescribe(Seg seg, mps_lib_FILE *stream, Count depth) } } res = WriteF(stream, 0, "$C", (WriteFC)c, NULL); - if(res != ResOK) + if (res != ResOK) return res; } res = WriteF(stream, 0, "\n", NULL); - if(res != ResOK) + if (res != ResOK) return res; } @@ -333,25 +344,28 @@ static Res AMCSegDescribe(Seg seg, mps_lib_FILE *stream, Count depth) if(res != ResOK) return res; - res = WriteF(stream, depth, "} AMC Seg $P\n", (WriteFP)seg, NULL); - if(res != ResOK) - return res; - return ResOK; } /* amcSegClass -- Class definition for AMC segments */ -DEFINE_SEG_CLASS(amcSegClass, class) +DEFINE_CLASS(Seg, amcSeg, klass) { - INHERIT_CLASS(class, GCSegClass); - SegClassMixInNoSplitMerge(class); /* no support for this (yet) */ - class->name = "AMCSEG"; - class->size = sizeof(amcSegStruct); - class->init = AMCSegInit; - class->describe = AMCSegDescribe; - AVERT(SegClass, class); + INHERIT_CLASS(klass, amcSeg, MutatorSeg); + SegClassMixInNoSplitMerge(klass); /* no support for this (yet) */ + klass->instClassStruct.describe = AMCSegDescribe; + klass->instClassStruct.finish = amcSegFinish; + klass->size = sizeof(amcSegStruct); + klass->init = AMCSegInit; + klass->bufferEmpty = amcSegBufferEmpty; + klass->whiten = amcSegWhiten; + klass->scan = amcSegScan; + klass->fix = amcSegFix; + klass->fixEmergency = amcSegFixEmergency; + klass->reclaim = amcSegReclaim; + klass->walk = amcSegWalk; + AVERT(SegClass, klass); } @@ -362,7 +376,7 @@ DEFINE_SEG_CLASS(amcSegClass, class) */ static Bool amcSegHasNailboard(Seg seg) { - amcSeg amcseg = Seg2amcSeg(seg); + amcSeg amcseg = MustBeA(amcSeg, seg); return amcseg->board != NULL; } @@ -371,7 +385,7 @@ static Bool amcSegHasNailboard(Seg seg) static Nailboard amcSegNailboard(Seg seg) { - amcSeg amcseg = Seg2amcSeg(seg); + amcSeg amcseg = MustBeA(amcSeg, seg); AVER(amcSegHasNailboard(seg)); return amcseg->board; } @@ -381,7 +395,7 @@ static Nailboard amcSegNailboard(Seg seg) static amcGen amcSegGen(Seg seg) { - amcSeg amcseg = Seg2amcSeg(seg); + amcSeg amcseg = MustBeA(amcSeg, seg); return amcseg->gen; } @@ -411,9 +425,6 @@ typedef struct AMCStruct { /* */ Sig sig; /* */ } AMCStruct; -#define PoolAMC(pool) PARENT(AMCStruct, poolStruct, (pool)) -#define AMCPool(amc) (&(amc)->poolStruct) - /* amcGenCheck -- check consistency of a generation structure */ @@ -450,18 +461,6 @@ typedef struct amcBufStruct { } amcBufStruct; -/* Buffer2amcBuf -- convert generic Buffer to an amcBuf */ - -#define Buffer2amcBuf(buffer) \ - PARENT(amcBufStruct, segbufStruct, \ - PARENT(SegBufStruct, bufferStruct, buffer)) - -/* amcBuf2Buffer -- convert amcBuf to generic Buffer */ - -#define amcBuf2Buffer(amcbuf) (&(amcbuf)->segbufStruct.bufferStruct) - - - /* amcBufCheck -- check consistency of an amcBuf */ ATTRIBUTE_UNUSED @@ -473,7 +472,7 @@ static Bool amcBufCheck(amcBuf amcbuf) CHECKD(amcGen, amcbuf->gen); CHECKL(BoolCheck(amcbuf->forHashArrays)); /* hash array buffers only created by mutator */ - CHECKL(BufferIsMutator(amcBuf2Buffer(amcbuf)) || !amcbuf->forHashArrays); + CHECKL(BufferIsMutator(MustBeA(Buffer, amcbuf)) || !amcbuf->forHashArrays); return TRUE; } @@ -482,7 +481,7 @@ static Bool amcBufCheck(amcBuf amcbuf) static amcGen amcBufGen(Buffer buffer) { - return Buffer2amcBuf(buffer)->gen; + return MustBeA(amcBuf, buffer)->gen; } @@ -490,11 +489,9 @@ static amcGen amcBufGen(Buffer buffer) static void amcBufSetGen(Buffer buffer, amcGen gen) { - amcBuf amcbuf; - - if(gen != NULL) + amcBuf amcbuf = MustBeA(amcBuf, buffer); + if (gen != NULL) AVERT(amcGen, gen); - amcbuf = Buffer2amcBuf(buffer); amcbuf->gen = gen; } @@ -505,31 +502,24 @@ ARG_DEFINE_KEY(ap_hash_arrays, Bool); /* AMCBufInit -- Initialize an amcBuf */ -static Res AMCBufInit(Buffer buffer, Pool pool, ArgList args) +static Res AMCBufInit(Buffer buffer, Pool pool, Bool isMutator, ArgList args) { - AMC amc; + AMC amc = MustBeA(AMCZPool, pool); amcBuf amcbuf; - BufferClass superclass; Res res; Bool forHashArrays = FALSE; ArgStruct arg; - AVERT(Buffer, buffer); - AVERT(Pool, pool); - amc = PoolAMC(pool); - AVERT(AMC, amc); - if (ArgPick(&arg, args, amcKeyAPHashArrays)) forHashArrays = arg.val.b; /* call next method */ - superclass = BUFFER_SUPERCLASS(amcBufClass); - res = (*superclass->init)(buffer, pool, args); + res = NextMethod(Buffer, amcBuf, init)(buffer, pool, isMutator, args); if(res != ResOK) return res; + amcbuf = CouldBeA(amcBuf, buffer); - amcbuf = Buffer2amcBuf(buffer); - if(BufferIsMutator(buffer)) { + if (BufferIsMutator(buffer)) { /* Set up the buffer to be allocating in the nursery. */ amcbuf->gen = amc->nursery; } else { @@ -537,8 +527,10 @@ static Res AMCBufInit(Buffer buffer, Pool pool, ArgList args) amcbuf->gen = NULL; } amcbuf->forHashArrays = forHashArrays; + + SetClassOfPoly(buffer, CLASS(amcBuf)); amcbuf->sig = amcBufSig; - AVERT(amcBuf, amcbuf); + AVERC(amcBuf, amcbuf); BufferSetRankSet(buffer, amc->rankSet); @@ -548,33 +540,24 @@ static Res AMCBufInit(Buffer buffer, Pool pool, ArgList args) /* AMCBufFinish -- Finish an amcBuf */ -static void AMCBufFinish(Buffer buffer) +static void AMCBufFinish(Inst inst) { - BufferClass super; - amcBuf amcbuf; - - AVERT(Buffer, buffer); - amcbuf = Buffer2amcBuf(buffer); - AVERT(amcBuf, amcbuf); - + Buffer buffer = MustBeA(Buffer, inst); + amcBuf amcbuf = MustBeA(amcBuf, buffer); amcbuf->sig = SigInvalid; - - /* Finish the superclass fields last. */ - super = BUFFER_SUPERCLASS(amcBufClass); - super->finish(buffer); + NextMethod(Inst, amcBuf, finish)(inst); } /* amcBufClass -- The class definition */ -DEFINE_BUFFER_CLASS(amcBufClass, class) +DEFINE_CLASS(Buffer, amcBuf, klass) { - INHERIT_CLASS(class, SegBufClass); - class->name = "AMCBUF"; - class->size = sizeof(amcBufStruct); - class->init = AMCBufInit; - class->finish = AMCBufFinish; - AVERT(BufferClass, class); + INHERIT_CLASS(klass, amcBuf, SegBuf); + klass->instClassStruct.finish = AMCBufFinish; + klass->size = sizeof(amcBufStruct); + klass->init = AMCBufInit; + AVERT(BufferClass, klass); } @@ -582,22 +565,21 @@ DEFINE_BUFFER_CLASS(amcBufClass, class) static Res amcGenCreate(amcGen *genReturn, AMC amc, GenDesc gen) { + Pool pool = MustBeA(AbstractPool, amc); Arena arena; Buffer buffer; - Pool pool; amcGen amcgen; Res res; void *p; - pool = AMCPool(amc); arena = pool->arena; - res = ControlAlloc(&p, arena, sizeof(amcGenStruct), FALSE); + res = ControlAlloc(&p, arena, sizeof(amcGenStruct)); if(res != ResOK) goto failControlAlloc; amcgen = (amcGen)p; - res = BufferCreate(&buffer, EnsureamcBufClass(), pool, FALSE, argsNone); + res = BufferCreate(&buffer, CLASS(amcBuf), pool, FALSE, argsNone); if(res != ResOK) goto failBufferCreate; @@ -671,14 +653,14 @@ static Res amcGenDescribe(amcGen gen, mps_lib_FILE *stream, Count depth) /* amcSegCreateNailboard -- create nailboard for segment */ -static Res amcSegCreateNailboard(Seg seg, Pool pool) +static Res amcSegCreateNailboard(Seg seg) { - amcSeg amcseg; + amcSeg amcseg = MustBeA(amcSeg, seg); + Pool pool = SegPool(seg); Nailboard board; Arena arena; Res res; - amcseg = Seg2amcSeg(seg); AVER(!amcSegHasNailboard(seg)); arena = PoolArena(pool); @@ -686,7 +668,9 @@ static Res amcSegCreateNailboard(Seg seg, Pool pool) SegBase(seg), SegLimit(seg)); if (res != ResOK) return res; + amcseg->board = board; + return ResOK; } @@ -695,7 +679,7 @@ static Res amcSegCreateNailboard(Seg seg, Pool pool) static Bool amcPinnedInterior(AMC amc, Nailboard board, Addr base, Addr limit) { - Size headerSize = AMCPool(amc)->format->headerSize; + Size headerSize = MustBeA(AbstractPool, amc)->format->headerSize; return !NailboardIsResRange(board, AddrSub(base, headerSize), AddrSub(limit, headerSize)); } @@ -729,11 +713,11 @@ static void AMCVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) * See . * Shared by AMCInit and AMCZinit. */ -static Res amcInitComm(Pool pool, RankSet rankSet, ArgList args) +static Res amcInitComm(Pool pool, Arena arena, PoolClass klass, + RankSet rankSet, ArgList args) { AMC amc; Res res; - Arena arena; Index i; size_t genArraySize; size_t genCount; @@ -744,12 +728,11 @@ static Res amcInitComm(Pool pool, RankSet rankSet, ArgList args) ArgStruct arg; AVER(pool != NULL); - - amc = PoolAMC(pool); - arena = PoolArena(pool); - - ArgRequire(&arg, args, MPS_KEY_FORMAT); - pool->format = arg.val.format; + AVERT(Arena, arena); + AVERT(ArgList, args); + AVERT(PoolClass, klass); + AVER(IsSubclass(klass, AMCZPool)); + if (ArgPick(&arg, args, MPS_KEY_CHAIN)) chain = arg.val.chain; else @@ -761,8 +744,6 @@ static Res amcInitComm(Pool pool, RankSet rankSet, ArgList args) if (ArgPick(&arg, args, MPS_KEY_LARGE_SIZE)) largeSize = arg.val.size; - AVERT(Format, pool->format); - AVER(FormatArena(pool->format) == arena); AVERT(Chain, chain); AVER(chain->arena == arena); AVER(extendBy > 0); @@ -772,8 +753,17 @@ static Res amcInitComm(Pool pool, RankSet rankSet, ArgList args) * unacceptable fragmentation due to the padding objects. This * assertion catches this bad case. */ AVER(largeSize >= extendBy); + + res = NextMethod(Pool, AMCZPool, init)(pool, arena, klass, args); + if (res != ResOK) + goto failNextInit; + amc = CouldBeA(AMCZPool, pool); + + /* Ensure a format was supplied in the argument list. */ + AVER(pool->format != NULL); + pool->alignment = pool->format->alignment; - pool->fix = AMCFix; + pool->alignShift = SizeLog2(pool->alignment); amc->rankSet = rankSet; RingInit(&amc->genRing); @@ -797,8 +787,9 @@ static Res amcInitComm(Pool pool, RankSet rankSet, ArgList args) amc->extendBy = SizeArenaGrains(extendBy, arena); amc->largeSize = largeSize; + SetClassOfPoly(pool, klass); amc->sig = AMCSig; - AVERT(AMC, amc); + AVERC(AMCZPool, amc); /* Init generations. */ genCount = ChainGens(chain); @@ -807,7 +798,7 @@ static Res amcInitComm(Pool pool, RankSet rankSet, ArgList args) /* One gen for each one in the chain plus dynamic gen. */ genArraySize = sizeof(amcGen) * (genCount + 1); - res = ControlAlloc(&p, arena, genArraySize, FALSE); + res = ControlAlloc(&p, arena, genArraySize); if(res != ResOK) goto failGensAlloc; amc->gen = p; @@ -843,17 +834,26 @@ static Res amcInitComm(Pool pool, RankSet rankSet, ArgList args) } ControlFree(arena, amc->gen, genArraySize); failGensAlloc: + NextMethod(Inst, AMCZPool, finish)(MustBeA(Inst, pool)); +failNextInit: + AVER(res != ResOK); return res; } -static Res AMCInit(Pool pool, ArgList args) +/* TODO: AMCInit should call AMCZInit (its superclass) then + specialize, but amcInitComm creates forwarding buffers that copy + the rank set from the pool, making this awkward. */ + +static Res AMCInit(Pool pool, Arena arena, PoolClass klass, ArgList args) { - return amcInitComm(pool, RankSetSingle(RankEXACT), args); + UNUSED(klass); /* used for debug pools only */ + return amcInitComm(pool, arena, CLASS(AMCPool), RankSetSingle(RankEXACT), args); } -static Res AMCZInit(Pool pool, ArgList args) +static Res AMCZInit(Pool pool, Arena arena, PoolClass klass, ArgList args) { - return amcInitComm(pool, RankSetEMPTY, args); + UNUSED(klass); /* used for debug pools only */ + return amcInitComm(pool, arena, CLASS(AMCZPool), RankSetEMPTY, args); } @@ -861,16 +861,13 @@ static Res AMCZInit(Pool pool, ArgList args) * * See . */ -static void AMCFinish(Pool pool) +static void AMCFinish(Inst inst) { - AMC amc; + Pool pool = MustBeA(AbstractPool, inst); + AMC amc = MustBeA(AMCZPool, pool); Ring ring; Ring node, nextNode; - AVERT(Pool, pool); - amc = PoolAMC(pool); - AVERT(AMC, amc); - EVENT1(AMCFinish, amc); /* @@@@ Make sure that segments aren't buffered by forwarding */ @@ -886,8 +883,9 @@ static void AMCFinish(Pool pool) RING_FOR(node, ring, nextNode) { Seg seg = SegOfPoolRing(node); amcGen gen = amcSegGen(seg); - amcSeg amcseg = Seg2amcSeg(seg); + amcSeg amcseg = MustBeA(amcSeg, seg); AVERT(amcSeg, amcseg); + AVER(!amcseg->accountedAsBuffered); PoolGenFree(&gen->pgen, seg, 0, amcseg->old ? SegSize(seg) : 0, @@ -908,6 +906,8 @@ static void AMCFinish(Pool pool) } amc->sig = SigInvalid; + + NextMethod(Inst, AMCZPool, finish)(inst); } @@ -916,35 +916,28 @@ static void AMCFinish(Pool pool) * See . */ static Res AMCBufferFill(Addr *baseReturn, Addr *limitReturn, - Pool pool, Buffer buffer, Size size, - Bool withReservoirPermit) + Pool pool, Buffer buffer, Size size) { Seg seg; - AMC amc; + AMC amc = MustBeA(AMCZPool, pool); Res res; Addr base, limit; Arena arena; Size grainsSize; amcGen gen; PoolGen pgen; - amcBuf amcbuf; + amcBuf amcbuf = MustBeA(amcBuf, buffer); - AVERT(Pool, pool); - amc = PoolAMC(pool); - AVERT(AMC, amc); AVER(baseReturn != NULL); AVER(limitReturn != NULL); AVERT(Buffer, buffer); AVER(BufferIsReset(buffer)); AVER(size > 0); AVER(SizeIsAligned(size, PoolAlignment(pool))); - AVERT(Bool, withReservoirPermit); arena = PoolArena(pool); gen = amcBufGen(buffer); AVERT(amcGen, gen); - amcbuf = Buffer2amcBuf(buffer); - AVERT(amcBuf, amcbuf); pgen = &gen->pgen; /* Create and attach segment. The location of this segment is */ @@ -957,8 +950,7 @@ static Res AMCBufferFill(Addr *baseReturn, Addr *limitReturn, } MPS_ARGS_BEGIN(args) { MPS_ARGS_ADD_FIELD(args, amcKeySegGen, p, gen); - res = PoolGenAlloc(&seg, pgen, amcSegClassGet(), grainsSize, - withReservoirPermit, args); + res = PoolGenAlloc(&seg, pgen, CLASS(amcSeg), grainsSize, args); } MPS_ARGS_END(args); if(res != ResOK) return res; @@ -977,7 +969,7 @@ static Res AMCBufferFill(Addr *baseReturn, Addr *limitReturn, && gen == amc->rampGen) || amcbuf->forHashArrays) { - Seg2amcSeg(seg)->deferred = TRUE; + MustBeA(amcSeg, seg)->deferred = TRUE; } base = SegBase(seg); @@ -1003,35 +995,37 @@ static Res AMCBufferFill(Addr *baseReturn, Addr *limitReturn, } } - PoolGenAccountForFill(pgen, SegSize(seg), Seg2amcSeg(seg)->deferred); + PoolGenAccountForFill(pgen, SegSize(seg)); + MustBeA(amcSeg, seg)->accountedAsBuffered = TRUE; + *baseReturn = base; *limitReturn = limit; return ResOK; } -/* amcBufferEmpty -- detach a buffer from a segment +/* amcSegBufferEmpty -- free from buffer to segment * * See . */ -static void AMCBufferEmpty(Pool pool, Buffer buffer, - Addr init, Addr limit) +static void amcSegBufferEmpty(Seg seg, Buffer buffer) { - AMC amc; - Size size; - Arena arena; - Seg seg; + amcSeg amcseg = MustBeA(amcSeg, seg); + Pool pool = SegPool(seg); + Arena arena = PoolArena(pool); + AMC amc = MustBeA(AMCZPool, pool); + Addr base, init, limit; + TraceId ti; + Trace trace; - AVERT(Pool, pool); - amc = PoolAMC(pool); - AVERT(AMC, amc); - AVERT(Buffer, buffer); - AVER(BufferIsReady(buffer)); - seg = BufferSeg(buffer); AVERT(Seg, seg); + AVERT(Buffer, buffer); + base = BufferBase(buffer); + init = BufferGetInit(buffer); + limit = BufferLimit(buffer); + AVER(SegBase(seg) <= base); + AVER(base <= init); AVER(init <= limit); - - arena = BufferArena(buffer); if(SegSize(seg) < amc->largeSize) { /* Small or Medium segment: buffer had the entire seg. */ AVER(limit == SegLimit(seg)); @@ -1041,17 +1035,26 @@ static void AMCBufferEmpty(Pool pool, Buffer buffer, } /* */ - size = AddrOffset(init, limit); - if(size > 0) { + if (init < limit) { ShieldExpose(arena, seg); - (*pool->format->pad)(init, size); + (*pool->format->pad)(init, AddrOffset(init, limit)); 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); + /* Any allocation in the buffer (including the padding object just + * created) is white, so needs to be accounted as condemned for all + * traces for which this segment is white. */ + TRACE_SET_ITER(ti, trace, seg->white, arena) + GenDescCondemned(amcseg->gen->pgen.gen, trace, + AddrOffset(base, limit)); + TRACE_SET_ITER_END(ti, trace, seg->white, arena); + + if (amcseg->accountedAsBuffered) { + /* Account the entire buffer (including the padding object) as used. */ + PoolGenAccountForEmpty(&amcseg->gen->pgen, SegSize(seg), 0, + amcseg->deferred); + amcseg->accountedAsBuffered = FALSE; + } } @@ -1059,11 +1062,8 @@ static void AMCBufferEmpty(Pool pool, Buffer buffer, static void AMCRampBegin(Pool pool, Buffer buf, Bool collectAll) { - AMC amc; + AMC amc = MustBeA(AMCZPool, pool); - AVERT(Pool, pool); - amc = PoolAMC(pool); - AVERT(AMC, amc); AVERT(Buffer, buf); AVERT(Bool, collectAll); UNUSED(collectAll); /* obsolete */ @@ -1081,11 +1081,8 @@ static void AMCRampBegin(Pool pool, Buffer buf, Bool collectAll) static void AMCRampEnd(Pool pool, Buffer buf) { - AMC amc; + AMC amc = MustBeA(AMCZPool, pool); - AVERT(Pool, pool); - amc = PoolAMC(pool); - AVERT(AMC, amc); AVERT(Buffer, buf); AVER(amc->rampCount > 0); @@ -1119,14 +1116,15 @@ static void AMCRampEnd(Pool pool, Buffer buf) * pool generation's sizes. */ RING_FOR(node, PoolSegRing(pool), nextNode) { Seg seg = SegOfPoolRing(node); - amcSeg amcseg = Seg2amcSeg(seg); + amcSeg amcseg = MustBeA(amcSeg, seg); if(amcSegGen(seg) == amc->rampGen && amcseg->deferred && SegWhite(seg) == TraceSetEMPTY) { - PoolGenUndefer(pgen, - amcseg->old ? SegSize(seg) : 0, - amcseg->old ? 0 : SegSize(seg)); + if (!amcseg->accountedAsBuffered) + PoolGenUndefer(pgen, + amcseg->old ? SegSize(seg) : 0, + amcseg->old ? 0 : SegSize(seg)); amcseg->deferred = FALSE; } } @@ -1134,27 +1132,35 @@ static void AMCRampEnd(Pool pool, Buffer buf) } -/* AMCWhiten -- condemn the segment for the trace +/* amcSegPoolGen -- get pool generation for a segment */ + +static PoolGen amcSegPoolGen(Pool pool, Seg seg) +{ + amcSeg amcseg = MustBeA(amcSeg, seg); + AVERT(Pool, pool); + AVER(pool == SegPool(seg)); + return &amcseg->gen->pgen; +} + + +/* amcSegWhiten -- condemn the segment for the trace * * If the segment has a mutator buffer on it, we nail the buffer, * because we can't scan or reclaim uncommitted buffers. */ -static Res AMCWhiten(Pool pool, Trace trace, Seg seg) +static Res amcSegWhiten(Seg seg, Trace trace) { Size condemned = 0; amcGen gen; - AMC amc; Buffer buffer; - amcSeg amcseg; + amcSeg amcseg = MustBeA(amcSeg, seg); + Pool pool = SegPool(seg); + AMC amc = MustBeA(AMCZPool, pool); Res res; - AVERT(Pool, pool); AVERT(Trace, trace); - AVERT(Seg, seg); - amcseg = Seg2amcSeg(seg); - buffer = SegBuffer(seg); - if(buffer != NULL) { + if (SegBuffer(&buffer, seg)) { AVERT(Buffer, buffer); if(!BufferIsMutator(buffer)) { /* forwarding buffer */ @@ -1174,20 +1180,21 @@ static Res AMCWhiten(Pool pool, Trace trace, Seg seg) /* BufferDetach(buffer, pool); */ /* } */ else { + Addr bufferScanLimit = BufferScanLimit(buffer); /* There is an active buffer, make sure it's nailed. */ if(!amcSegHasNailboard(seg)) { if(SegNailed(seg) == TraceSetEMPTY) { - res = amcSegCreateNailboard(seg, pool); + res = amcSegCreateNailboard(seg); if(res != ResOK) { /* Can't create nailboard, don't condemn. */ return ResOK; } - if(BufferScanLimit(buffer) != BufferLimit(buffer)) { + if (bufferScanLimit != BufferLimit(buffer)) { NailboardSetRange(amcSegNailboard(seg), - BufferScanLimit(buffer), + bufferScanLimit, BufferLimit(buffer)); } - ++trace->nailCount; + STATISTIC(++trace->nailCount); SegSetNailed(seg, TraceSetSingle(trace)); } else { /* Segment is nailed already, cannot create a nailboard */ @@ -1196,36 +1203,43 @@ 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) + AVER(bufferScanLimit == BufferLimit(buffer) || NailboardIsSetRange(amcSegNailboard(seg), - BufferScanLimit(buffer), + bufferScanLimit, BufferLimit(buffer))); /* Nail it for this trace as well. */ SegSetNailed(seg, TraceSetAdd(SegNailed(seg), trace)); } + /* Move the buffer's base up to the scan limit, so that we can + * detect allocation that happens during the trace, and + * account for it correctly in amcSegBufferEmpty and + * amcSegReclaimNailed. */ + buffer->base = bufferScanLimit; /* We didn't condemn the buffer, subtract it from the count. */ - /* @@@@ We could subtract all the nailed grains. */ /* Relies on unsigned arithmetic wrapping round */ /* on under- and overflow (which it does). */ - condemned -= AddrOffset(BufferScanLimit(buffer), BufferLimit(buffer)); + condemned -= AddrOffset(BufferBase(buffer), BufferLimit(buffer)); } } } - SegSetWhite(seg, TraceSetAdd(SegWhite(seg), trace)); - condemned += SegSize(seg); - trace->condemned += condemned; - - amc = PoolAMC(pool); - AVERT(AMC, amc); - gen = amcSegGen(seg); AVERT(amcGen, gen); if (!amcseg->old) { - PoolGenAccountForAge(&gen->pgen, SegSize(seg), amcseg->deferred); amcseg->old = TRUE; + if (amcseg->accountedAsBuffered) { + /* Note that the segment remains buffered but the buffer contents + * are accounted as old. See .seg.accounted-as-buffered. */ + amcseg->accountedAsBuffered = FALSE; + PoolGenAccountForAge(&gen->pgen, SegSize(seg), 0, amcseg->deferred); + } else + PoolGenAccountForAge(&gen->pgen, 0, SegSize(seg), amcseg->deferred); } + amcseg->forwarded[trace->ti] = 0; + SegSetWhite(seg, TraceSetAdd(SegWhite(seg), trace)); + GenDescCondemned(gen->pgen.gen, trace, condemned + SegSize(seg)); + /* Ensure we are forwarding into the right generation. */ /* see */ @@ -1245,21 +1259,20 @@ static Res AMCWhiten(Pool pool, Trace trace, Seg seg) } -/* amcScanNailedRange -- make one scanning pass over a range of +/* amcSegScanNailedRange -- 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, - ScanState ss, - AMC amc, Nailboard board, - Addr base, Addr limit) +static Res amcSegScanNailedRange(Bool *totalReturn, Bool *moreReturn, + ScanState ss, AMC amc, Nailboard board, + Addr base, Addr limit) { Format format; Size headerSize; Addr p, clientLimit; - Pool pool = AMCPool(amc); + Pool pool = MustBeA(AbstractPool, amc); format = pool->format; headerSize = format->headerSize; p = AddrAdd(base, headerSize); @@ -1285,7 +1298,7 @@ static Res amcScanNailedRange(Bool *totalReturn, Bool *moreReturn, } -/* amcScanNailedOnce -- make one scanning pass over a nailed segment +/* amcSegScanNailedOnce -- make one scanning pass over a nailed segment * * *totalReturn is set to TRUE iff all objects in segment scanned. * *moreReturn is set to FALSE only if there are no more objects @@ -1294,12 +1307,13 @@ static Res amcScanNailedRange(Bool *totalReturn, Bool *moreReturn, * also if during emergency fixing any new marks got added to the * nailboard. */ -static Res amcScanNailedOnce(Bool *totalReturn, Bool *moreReturn, - ScanState ss, Seg seg, AMC amc) +static Res amcSegScanNailedOnce(Bool *totalReturn, Bool *moreReturn, + ScanState ss, Seg seg, AMC amc) { Addr p, limit; Nailboard board; Res res; + Buffer buffer; EVENT3(AMCScanBegin, amc, seg, ss); /* TODO: consider using own event */ @@ -1308,14 +1322,14 @@ static Res amcScanNailedOnce(Bool *totalReturn, Bool *moreReturn, NailboardClearNewNails(board); p = SegBase(seg); - while(SegBuffer(seg) != NULL) { - limit = BufferScanLimit(SegBuffer(seg)); + while (SegBuffer(&buffer, seg)) { + limit = BufferScanLimit(buffer); if(p >= limit) { AVER(p == limit); goto returnGood; } - res = amcScanNailedRange(totalReturn, moreReturn, - ss, amc, board, p, limit); + res = amcSegScanNailedRange(totalReturn, moreReturn, + ss, amc, board, p, limit); if (res != ResOK) return res; p = limit; @@ -1323,8 +1337,8 @@ static Res amcScanNailedOnce(Bool *totalReturn, Bool *moreReturn, limit = SegLimit(seg); /* @@@@ Shouldn't p be set to BufferLimit here?! */ - res = amcScanNailedRange(totalReturn, moreReturn, - ss, amc, board, p, limit); + res = amcSegScanNailedRange(totalReturn, moreReturn, + ss, amc, board, p, limit); if (res != ResOK) return res; @@ -1336,17 +1350,17 @@ static Res amcScanNailedOnce(Bool *totalReturn, Bool *moreReturn, } -/* amcScanNailed -- scan a nailed segment */ +/* amcSegScanNailed -- scan a nailed segment */ -static Res amcScanNailed(Bool *totalReturn, ScanState ss, Pool pool, - Seg seg, AMC amc) +static Res amcSegScanNailed(Bool *totalReturn, ScanState ss, Pool pool, + Seg seg, AMC amc) { Bool total, moreScanning; size_t loops = 0; do { Res res; - res = amcScanNailedOnce(&total, &moreScanning, ss, seg, amc); + res = amcSegScanNailedOnce(&total, &moreScanning, ss, seg, amc); if(res != ResOK) { *totalReturn = FALSE; return res; @@ -1382,37 +1396,37 @@ static Res amcScanNailed(Bool *totalReturn, ScanState ss, Pool pool, } -/* AMCScan -- scan a single seg, turning it black +/* amcSegScan -- scan a single seg, turning it black * * See . */ -static Res AMCScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg) +static Res amcSegScan(Bool *totalReturn, Seg seg, ScanState ss) { Addr base, limit; Format format; + Pool pool; AMC amc; Res res; + Buffer buffer; AVER(totalReturn != NULL); - AVERT(ScanState, ss); AVERT(Seg, seg); - AVERT(Pool, pool); - amc = PoolAMC(pool); - AVERT(AMC, amc); - + AVERT(ScanState, ss); + pool = SegPool(seg); + amc = MustBeA(AMCZPool, pool); format = pool->format; if(amcSegHasNailboard(seg)) { - return amcScanNailed(totalReturn, ss, pool, seg, amc); + return amcSegScanNailed(totalReturn, ss, pool, seg, amc); } EVENT3(AMCScanBegin, amc, seg, ss); base = AddrAdd(SegBase(seg), format->headerSize); /* */ - while(SegBuffer(seg) != NULL) { - limit = AddrAdd(BufferScanLimit(SegBuffer(seg)), + while (SegBuffer(&buffer, seg)) { + limit = AddrAdd(BufferScanLimit(buffer), format->headerSize); if(base >= limit) { /* @@@@ Are we sure we don't need scan the rest of the */ @@ -1448,7 +1462,7 @@ static Res AMCScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg) } -/* amcFixInPlace -- fix an reference without moving the object +/* amcSegFixInPlace -- fix a reference without moving the object * * Usually this function is used for ambiguous references, but during * emergency tracing may be used for references of any rank. @@ -1456,12 +1470,10 @@ static Res AMCScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg) * If the segment has a nailboard then we use that to record the fix. * Otherwise we simply grey and nail the entire segment. */ -static void amcFixInPlace(Pool pool, Seg seg, ScanState ss, Ref *refIO) +static void amcSegFixInPlace(Seg seg, ScanState ss, Ref *refIO) { Addr ref; - UNUSED(pool); - ref = (Addr)*refIO; /* An ambiguous reference can point before the header. */ AVER(SegBase(seg) <= ref); @@ -1489,28 +1501,22 @@ static void amcFixInPlace(Pool pool, Seg seg, ScanState ss, Ref *refIO) } -/* AMCFixEmergency -- fix a reference, without allocating +/* amcSegFixEmergency -- fix a reference, without allocating * * See . */ -static Res AMCFixEmergency(Pool pool, ScanState ss, Seg seg, - Ref *refIO) +static Res amcSegFixEmergency(Seg seg, ScanState ss, Ref *refIO) { Arena arena; - AMC amc; Addr newRef; + Pool pool; - AVERT(Pool, pool); - AVERT(ScanState, ss); AVERT(Seg, seg); + AVERT(ScanState, ss); AVER(refIO != NULL); + pool = SegPool(seg); arena = PoolArena(pool); - AVERT(Arena, arena); - amc = PoolAMC(pool); - AVERT(AMC, amc); - - ss->wasMarked = TRUE; if(ss->rank == RankAMBIG) goto fixInPlace; @@ -1528,18 +1534,19 @@ static Res AMCFixEmergency(Pool pool, ScanState ss, Seg seg, } fixInPlace: /* see .Nailboard.emergency */ - amcFixInPlace(pool, seg, ss, refIO); + amcSegFixInPlace(seg, ss, refIO); return ResOK; } -/* AMCFix -- fix a reference to the pool +/* amcSegFix -- fix a reference to the segment * * See . */ -static Res AMCFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) +static Res amcSegFix(Seg seg, ScanState ss, Ref *refIO) { Arena arena; + Pool pool; AMC amc; Res res; Format format; /* cache of pool->format */ @@ -1553,18 +1560,15 @@ static Res AMCFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) amcGen gen; /* generation of old copy of object */ TraceSet grey; /* greyness of object being relocated */ Seg toSeg; /* segment to which object is being relocated */ + TraceId ti; + Trace trace; /* */ - 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 */ @@ -1574,21 +1578,22 @@ static Res AMCFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) /* 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 */ + /* assumptions in amcSegFixEmergency will be wrong (essentially */ /* we will lose some pointer fixes because we introduced a */ /* nailboard). */ if(SegNailed(seg) == TraceSetEMPTY) { - res = amcSegCreateNailboard(seg, pool); + res = amcSegCreateNailboard(seg); if(res != ResOK) return res; - ++ss->nailCount; + STATISTIC(++ss->nailCount); SegSetNailed(seg, TraceSetUnion(SegNailed(seg), ss->traces)); } - amcFixInPlace(pool, seg, ss, refIO); + amcSegFixInPlace(seg, ss, refIO); return ResOK; } - amc = PoolAMC(pool); + pool = SegPool(seg); + amc = MustBeA_CRITICAL(AMCZPool, pool); AVERT_CRITICAL(AMC, amc); format = pool->format; headerSize = format->headerSize; @@ -1630,8 +1635,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. */ - /* */ - ss->wasMarked = FALSE; + ss->wasMarked = FALSE; /* */ /* Get the forwarding buffer from the object's generation. */ gen = amcSegGen(seg); @@ -1639,10 +1643,9 @@ static Res AMCFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) AVER_CRITICAL(buffer != NULL); length = AddrOffset(ref, clientQ); /* .exposed.seg */ - STATISTIC_STAT(++ss->forwardedCount); - ss->forwardedSize += length; + STATISTIC(++ss->forwardedCount); do { - res = BUFFER_RESERVE(&newBase, buffer, length, FALSE); + res = BUFFER_RESERVE(&newBase, buffer, length); if (res != ResOK) goto returnRes; newRef = AddrAdd(newBase, headerSize); @@ -1657,7 +1660,7 @@ static Res AMCFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) grey = TraceSetUnion(grey, ss->traces); SegSetSummary(toSeg, RefSetUnion(SegSummary(toSeg), SegSummary(seg))); } else { - AVER(SegRankSet(toSeg) == RankSetEMPTY); + AVER_CRITICAL(SegRankSet(toSeg) == RankSetEMPTY); } SegSetGrey(toSeg, TraceSetUnion(SegGrey(toSeg), grey)); @@ -1666,7 +1669,11 @@ static Res AMCFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) ShieldCover(arena, toSeg); } while (!BUFFER_COMMIT(buffer, newBase, length)); - ss->copiedSize += length; + + STATISTIC(ss->copiedSize += length); + TRACE_SET_ITER(ti, trace, ss->traces, ss->arena) + MustBeA(amcSeg, seg)->forwarded[ti] += length; + TRACE_SET_ITER_END(ti, trace, ss->traces, ss->arena); (*format->move)(ref, newRef); /* .exposed.seg */ @@ -1674,7 +1681,7 @@ static Res AMCFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) } else { /* reference to broken heart (which should be snapped out -- */ /* consider adding to (non-existent) snap-out cache here) */ - STATISTIC_STAT(++ss->snapCount); + STATISTIC(++ss->snapCount); } /* .fix.update: update the reference to whatever the above code */ @@ -1689,25 +1696,25 @@ static Res AMCFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) } -/* amcReclaimNailed -- reclaim what you can from a nailed segment */ +/* amcSegReclaimNailed -- reclaim what you can from a nailed segment */ -static void amcReclaimNailed(Pool pool, Trace trace, Seg seg) +static void amcSegReclaimNailed(Pool pool, Trace trace, Seg seg) { Addr p, limit; Arena arena; Format format; - Size bytesReclaimed = (Size)0; + STATISTIC_DECL(Size bytesReclaimed = (Size)0) Count preservedInPlaceCount = (Count)0; Size preservedInPlaceSize = (Size)0; - AMC amc; + AMC amc = MustBeA(AMCZPool, pool); + PoolGen pgen; Size headerSize; Addr padBase; /* base of next padding object */ Size padLength; /* length of next padding object */ + Buffer buffer; /* All arguments AVERed by AMCReclaim */ - amc = PoolAMC(pool); - AVERT(AMC, amc); format = pool->format; arena = PoolArena(pool); @@ -1717,11 +1724,7 @@ static void amcReclaimNailed(Pool pool, Trace trace, Seg seg) headerSize = format->headerSize; ShieldExpose(arena, seg); p = SegBase(seg); - if(SegBuffer(seg) != NULL) { - limit = BufferScanLimit(SegBuffer(seg)); - } else { - limit = SegLimit(seg); - } + limit = SegBufferScanLimit(seg); padBase = p; padLength = 0; while(p < limit) { @@ -1747,7 +1750,7 @@ static void amcReclaimNailed(Pool pool, Trace trace, Seg seg) /* Replace run of forwarding pointers and unreachable objects * with a padding object. */ (*format->pad)(padBase, padLength); - bytesReclaimed += padLength; + STATISTIC(bytesReclaimed += padLength); padLength = 0; } padBase = q; @@ -1764,7 +1767,7 @@ static void amcReclaimNailed(Pool pool, Trace trace, Seg seg) /* Replace final run of forwarding pointers and unreachable * objects with a padding object. */ (*format->pad)(padBase, padLength); - bytesReclaimed += padLength; + STATISTIC(bytesReclaimed += padLength); } ShieldCover(arena, seg); @@ -1772,44 +1775,47 @@ static void amcReclaimNailed(Pool pool, Trace trace, Seg seg) SegSetWhite(seg, TraceSetDel(SegWhite(seg), trace)); if(SegNailed(seg) == TraceSetEMPTY && amcSegHasNailboard(seg)) { NailboardDestroy(amcSegNailboard(seg), arena); - Seg2amcSeg(seg)->board = NULL; + MustBeA(amcSeg, seg)->board = NULL; } - AVER(bytesReclaimed <= SegSize(seg)); - trace->reclaimSize += bytesReclaimed; - trace->preservedInPlaceCount += preservedInPlaceCount; - trace->preservedInPlaceSize += preservedInPlaceSize; + STATISTIC(AVER(bytesReclaimed <= SegSize(seg))); + STATISTIC(trace->reclaimSize += bytesReclaimed); + STATISTIC(trace->preservedInPlaceCount += preservedInPlaceCount); + pgen = &amcSegGen(seg)->pgen; + if (SegBuffer(&buffer, seg)) { + /* Any allocation in the buffer was white, so needs to be + * accounted as condemned now. */ + GenDescCondemned(pgen->gen, trace, + AddrOffset(BufferBase(buffer), BufferLimit(buffer))); + } + GenDescSurvived(pgen->gen, trace, MustBeA(amcSeg, seg)->forwarded[trace->ti], + preservedInPlaceSize); /* Free the seg if we can; fixes .nailboard.limitations.middle. */ if(preservedInPlaceCount == 0 - && (SegBuffer(seg) == NULL) + && (!SegHasBuffer(seg)) && (SegNailed(seg) == TraceSetEMPTY)) { - amcGen gen = amcSegGen(seg); - /* We may not free a buffered seg. */ - AVER(SegBuffer(seg) == NULL); + AVER(!SegHasBuffer(seg)); - PoolGenFree(&gen->pgen, seg, 0, SegSize(seg), 0, Seg2amcSeg(seg)->deferred); + PoolGenFree(pgen, seg, 0, SegSize(seg), 0, MustBeA(amcSeg, seg)->deferred); } } -/* AMCReclaim -- recycle a segment if it is still white +/* amcSegReclaim -- recycle a segment if it is still white * * See . */ -static void AMCReclaim(Pool pool, Trace trace, Seg seg) +static void amcSegReclaim(Seg seg, Trace trace) { - AMC amc; + amcSeg amcseg = MustBeA_CRITICAL(amcSeg, seg); + Pool pool = SegPool(seg); + AMC amc = MustBeA_CRITICAL(AMCZPool, pool); amcGen gen; - AVERT_CRITICAL(Pool, pool); - amc = PoolAMC(pool); - AVERT_CRITICAL(AMC, amc); AVERT_CRITICAL(Trace, trace); - AVERT_CRITICAL(Seg, seg); - gen = amcSegGen(seg); AVERT_CRITICAL(amcGen, gen); @@ -1827,31 +1833,28 @@ static void AMCReclaim(Pool pool, Trace trace, Seg seg) } if(SegNailed(seg) != TraceSetEMPTY) { - amcReclaimNailed(pool, trace, seg); + amcSegReclaimNailed(pool, trace, seg); return; } /* We may not free a buffered seg. (But all buffered + condemned */ /* segs should have been nailed anyway). */ - AVER(SegBuffer(seg) == NULL); + AVER(!SegHasBuffer(seg)); - trace->reclaimSize += SegSize(seg); + STATISTIC(trace->reclaimSize += SegSize(seg)); - PoolGenFree(&gen->pgen, seg, 0, SegSize(seg), 0, Seg2amcSeg(seg)->deferred); + GenDescSurvived(gen->pgen.gen, trace, amcseg->forwarded[trace->ti], 0); + PoolGenFree(&gen->pgen, seg, 0, SegSize(seg), 0, amcseg->deferred); } -/* AMCWalk -- Apply function to (black) objects in segment */ +/* amcSegWalk -- Apply function to (black) objects in segment */ -static void AMCWalk(Pool pool, Seg seg, FormattedObjectsVisitor f, - void *p, size_t s) +static void amcSegWalk(Seg seg, Format format, FormattedObjectsVisitor f, + void *p, size_t s) { - Addr object, nextObject, limit; - AMC amc; - Format format; - - AVERT(Pool, pool); AVERT(Seg, seg); + AVERT(Format, format); AVER(FUNCHECK(f)); /* p and s are arbitrary closures so can't be checked */ @@ -1865,24 +1868,16 @@ static void AMCWalk(Pool pool, Seg seg, FormattedObjectsVisitor f, if(SegWhite(seg) == TraceSetEMPTY && SegGrey(seg) == TraceSetEMPTY && SegNailed(seg) == TraceSetEMPTY) { - amc = PoolAMC(pool); - AVERT(AMC, amc); - format = pool->format; - - /* If the segment is buffered, only walk as far as the end */ - /* of the initialized objects. cf. AMCScan */ - if(SegBuffer(seg) != NULL) - limit = BufferScanLimit(SegBuffer(seg)); - else - limit = SegLimit(seg); - limit = AddrAdd(limit, format->headerSize); + Addr object, nextObject, limit; + Pool pool = SegPool(seg); + limit = AddrAdd(SegBufferScanLimit(seg), format->headerSize); object = AddrAdd(SegBase(seg), format->headerSize); while(object < limit) { /* Check not a broken heart. */ AVER((*format->isMoved)(object) == NULL); - (*f)(object, pool->format, pool, p, s); - nextObject = (*pool->format->skip)(object); + (*f)(object, format, pool, p, s); + nextObject = (*format->skip)(object); AVER(nextObject > object); object = nextObject; } @@ -1897,8 +1892,12 @@ static void amcWalkAll(Pool pool, FormattedObjectsVisitor f, void *p, size_t s) { Arena arena; Ring ring, next, node; + Format format = NULL; + Bool b; - AVER(IsSubclassPoly(pool->class, AMCZPoolClassGet())); + AVER(IsA(AMCZPool, pool)); + b = PoolFormat(&format, pool); + AVER(b); arena = PoolArena(pool); ring = PoolSegRing(pool); @@ -1907,105 +1906,20 @@ static void amcWalkAll(Pool pool, FormattedObjectsVisitor f, void *p, size_t s) Seg seg = SegOfPoolRing(node); ShieldExpose(arena, seg); - AMCWalk(pool, seg, f, p, s); + amcSegWalk(seg, format, f, p, s); ShieldCover(arena, seg); } } -/* amcAddrObjectSearch -- skip over objects (belonging to pool) - * starting at objBase until we reach one of the following cases: - * 1. addr is found (and not moved): set *pReturn to the client - * pointer to the object containing addr and return ResOK; - * 2. addr is found, but it moved: return ResFAIL; - * 3. we reach searchLimit: return ResFAIL. - */ -static Res amcAddrObjectSearch(Addr *pReturn, Pool pool, Addr objBase, - Addr searchLimit, Addr addr) -{ - Format format; - Size hdrSize; - - AVER(pReturn != NULL); - AVERT(Pool, pool); - AVER(objBase <= searchLimit); - - format = pool->format; - hdrSize = format->headerSize; - while (objBase < searchLimit) { - Addr objRef = AddrAdd(objBase, hdrSize); - Addr objLimit = AddrSub((*format->skip)(objRef), hdrSize); - AVER(objBase < objLimit); - if (addr < objLimit) { - AVER(objBase <= addr); - AVER(addr < objLimit); /* the point */ - if (!(*format->isMoved)(objRef)) { - *pReturn = objRef; - return ResOK; - } - break; - } - objBase = objLimit; - } - return ResFAIL; -} - - -/* AMCAddrObject -- find client pointer to object containing addr. - * addr is known to belong to seg, which belongs to pool. - * See job003589. - */ -static Res AMCAddrObject(Addr *pReturn, Pool pool, Seg seg, Addr addr) -{ - Res res; - Arena arena; - Addr base, limit; /* range of objects on segment */ - - AVER(pReturn != NULL); - AVERT(Pool, pool); - AVERT(Seg, seg); - AVER(SegPool(seg) == pool); - AVER(SegBase(seg) <= addr); - AVER(addr < SegLimit(seg)); - - arena = PoolArena(pool); - base = SegBase(seg); - if (SegBuffer(seg) != NULL) { - /* We use BufferGetInit here (and not BufferScanLimit) because we - * want to be able to find objects that have been allocated and - * committed since the last flip. These objects lie between the - * addresses returned by BufferScanLimit (which returns the value - * of init at the last flip) and BufferGetInit. - * - * Strictly speaking we only need a limit that is at least the - * maximum of the objects on the segments. This is because addr - * *must* point inside a live object and we stop skipping once we - * have found it. The init pointer serves this purpose. - */ - limit = BufferGetInit(SegBuffer(seg)); - } else { - limit = SegLimit(seg); - } - - ShieldExpose(arena, seg); - res = amcAddrObjectSearch(pReturn, pool, base, limit, addr); - ShieldCover(arena, seg); - return res; -} - - /* AMCTotalSize -- total memory allocated from the arena */ static Size AMCTotalSize(Pool pool) { - AMC amc; + AMC amc = MustBeA(AMCZPool, pool); 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); @@ -2020,14 +1934,10 @@ static Size AMCTotalSize(Pool pool) static Size AMCFreeSize(Pool pool) { - AMC amc; + AMC amc = MustBeA(AMCZPool, pool); 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); @@ -2042,27 +1952,22 @@ static Size AMCFreeSize(Pool pool) * * See . */ -static Res AMCDescribe(Pool pool, mps_lib_FILE *stream, Count depth) + +static Res AMCDescribe(Inst inst, mps_lib_FILE *stream, Count depth) { + Pool pool = CouldBeA(AbstractPool, inst); + AMC amc = CouldBeA(AMCZPool, pool); Res res; - AMC amc; Ring node, nextNode; const char *rampmode; - if(!TESTT(Pool, pool)) - return ResFAIL; - amc = PoolAMC(pool); - if(!TESTT(AMC, amc)) - return ResFAIL; - if(stream == NULL) - return ResFAIL; + if (!TESTC(AMCZPool, amc)) + return ResPARAM; + if (stream == NULL) + return ResPARAM; - res = WriteF(stream, depth, - (amc->rankSet == RankSetEMPTY) ? "AMCZ" : "AMC", - " $P {\n", (WriteFP)amc, " pool $P ($U)\n", - (WriteFP)AMCPool(amc), (WriteFU)AMCPool(amc)->serial, - NULL); - if(res != ResOK) + res = NextMethod(Inst, AMCZPool, describe)(inst, stream, depth); + if (res != ResOK) return res; switch(amc->rampMode) { @@ -2089,66 +1994,49 @@ static Res AMCDescribe(Pool pool, mps_lib_FILE *stream, Count depth) return res; } - if(0) { + if (0) { /* SegDescribes */ - RING_FOR(node, &AMCPool(amc)->segRing, nextNode) { + RING_FOR(node, &pool->segRing, nextNode) { Seg seg = RING_ELT(Seg, poolRing, node); - res = AMCSegDescribe(seg, stream, depth + 2); + res = SegDescribe(seg, stream, depth + 2); if(res != ResOK) return res; } } - res = WriteF(stream, depth, "} AMC $P\n", (WriteFP)amc, NULL); - if(res != ResOK) - return res; - return ResOK; } /* AMCZPoolClass -- the class definition */ -DEFINE_POOL_CLASS(AMCZPoolClass, this) +DEFINE_CLASS(Pool, AMCZPool, klass) { - INHERIT_CLASS(this, AbstractSegBufPoolClass); - PoolClassMixInFormat(this); - PoolClassMixInCollect(this); - this->name = "AMCZ"; - this->size = sizeof(AMCStruct); - this->offset = offsetof(AMCStruct, poolStruct); - this->attr |= AttrMOVINGGC; - this->varargs = AMCVarargs; - this->init = AMCZInit; - this->finish = AMCFinish; - this->bufferFill = AMCBufferFill; - this->bufferEmpty = AMCBufferEmpty; - this->whiten = AMCWhiten; - this->fix = AMCFix; - this->fixEmergency = AMCFixEmergency; - this->reclaim = AMCReclaim; - 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); + INHERIT_CLASS(klass, AMCZPool, AbstractCollectPool); + klass->instClassStruct.describe = AMCDescribe; + klass->instClassStruct.finish = AMCFinish; + klass->size = sizeof(AMCStruct); + klass->attr |= AttrMOVINGGC; + klass->varargs = AMCVarargs; + klass->init = AMCZInit; + klass->bufferFill = AMCBufferFill; + klass->rampBegin = AMCRampBegin; + klass->rampEnd = AMCRampEnd; + klass->segPoolGen = amcSegPoolGen; + klass->bufferClass = amcBufClassGet; + klass->totalSize = AMCTotalSize; + klass->freeSize = AMCFreeSize; + AVERT(PoolClass, klass); } /* AMCPoolClass -- the class definition */ -DEFINE_POOL_CLASS(AMCPoolClass, this) +DEFINE_CLASS(Pool, AMCPool, klass) { - INHERIT_CLASS(this, AMCZPoolClass); - PoolClassMixInScan(this); - this->name = "AMC"; - this->init = AMCInit; - this->scan = AMCScan; - AVERT(PoolClass, this); + INHERIT_CLASS(klass, AMCPool, AMCZPool); + klass->init = AMCInit; + AVERT(PoolClass, klass); } @@ -2156,14 +2044,14 @@ DEFINE_POOL_CLASS(AMCPoolClass, this) mps_pool_class_t mps_class_amc(void) { - return (mps_pool_class_t)AMCPoolClassGet(); + return (mps_pool_class_t)CLASS(AMCPool); } /* mps_class_amcz -- return the pool class descriptor to the client */ mps_pool_class_t mps_class_amcz(void) { - return (mps_pool_class_t)AMCZPoolClassGet(); + return (mps_pool_class_t)CLASS(AMCZPool); } @@ -2227,8 +2115,8 @@ ATTRIBUTE_UNUSED static Bool AMCCheck(AMC amc) { CHECKS(AMC, amc); - CHECKD(Pool, AMCPool(amc)); - CHECKL(IsSubclassPoly(AMCPool(amc)->class, AMCZPoolClassGet())); + CHECKC(AMCZPool, amc); + CHECKD(Pool, MustBeA(AbstractPool, amc)); CHECKL(RankSetCheck(amc->rankSet)); CHECKD_NOSIG(Ring, &amc->genRing); CHECKL(BoolCheck(amc->gensBooted)); @@ -2254,7 +2142,7 @@ static Bool AMCCheck(AMC amc) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2016 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/poolams.c b/mps/code/poolams.c index 40ca5f65426..9700e285a09 100644 --- a/mps/code/poolams.c +++ b/mps/code/poolams.c @@ -1,7 +1,7 @@ /* poolams.c: AUTOMATIC MARK & SWEEP POOL CLASS * * $Id$ - * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * Portions copyright (c) 2002 Global Graphics Software. * * @@ -26,6 +26,17 @@ SRCID(poolams, "$Id$"); #define AMSSig ((Sig)0x519A3599) /* SIGnature AMS */ #define AMSSegSig ((Sig)0x519A3559) /* SIGnature AMS SeG */ +static Bool amsSegBufferFill(Addr *baseReturn, Addr *limitReturn, + Seg seg, Size size, RankSet rankSet); +static void amsSegBufferEmpty(Seg seg, Buffer buffer); +static void amsSegBlacken(Seg seg, TraceSet traceSet); +static Res amsSegWhiten(Seg seg, Trace trace); +static Res amsSegScan(Bool *totalReturn, Seg seg, ScanState ss); +static Res amsSegFix(Seg seg, ScanState ss, Ref *refIO); +static void amsSegReclaim(Seg seg, Trace trace); +static void amsSegWalk(Seg seg, Format format, FormattedObjectsVisitor f, + void *p, size_t s); + /* AMSDebugStruct -- structure for a debug subclass */ @@ -46,16 +57,17 @@ typedef struct AMSDebugStruct *AMSDebug; Bool AMSSegCheck(AMSSeg amsseg) { - Seg seg = AMSSeg2Seg(amsseg); + Seg seg = MustBeA(Seg, amsseg); + Pool pool = SegPool(seg); CHECKS(AMSSeg, amsseg); CHECKD(GCSeg, &amsseg->gcSegStruct); CHECKU(AMS, amsseg->ams); CHECKL(AMSPool(amsseg->ams) == SegPool(seg)); - CHECKD_NOSIG(Ring, &amsseg->segRing); - CHECKL(amsseg->grains == AMSGrains(amsseg->ams, SegSize(seg))); + CHECKL(amsseg->grains == PoolSizeGrains(pool, SegSize(seg))); CHECKL(amsseg->grains > 0); - CHECKL(amsseg->grains == amsseg->freeGrains + amsseg->oldGrains + amsseg->newGrains); + CHECKL(amsseg->grains == amsseg->freeGrains + amsseg->bufferedGrains + + amsseg->oldGrains + amsseg->newGrains); CHECKL(BoolCheck(amsseg->allocTableInUse)); if (!amsseg->allocTableInUse) @@ -105,11 +117,13 @@ void AMSSegFreeWalk(AMSSeg amsseg, FreeBlockVisitor f, void *p) next, amsseg->grains, 1); if (!found) break; - (*f)(AMS_INDEX_ADDR(seg, base), AMS_INDEX_ADDR(seg, limit), pool, p); + (*f)(PoolAddrOfIndex(SegBase(seg), pool, base), + PoolAddrOfIndex(SegBase(seg), pool, limit), pool, p); next = limit + 1; } } else if (amsseg->firstFree < amsseg->grains) - (*f)(AMS_INDEX_ADDR(seg, amsseg->firstFree), SegLimit(seg), pool, p); + (*f)(PoolAddrOfIndex(SegBase(seg), pool, amsseg->firstFree), + SegLimit(seg), pool, p); } @@ -134,7 +148,7 @@ void AMSSegFreeCheck(AMSSeg amsseg) /* If it's not a debug class, don't bother walking. */ pool = SegPool(AMSSeg2Seg(amsseg)); AVERT(Pool, pool); - debug = ((pool)->class->debugMixin)(pool); + debug = Method(Pool, pool, debugMixin)(pool); if (debug == NULL) return; @@ -215,34 +229,30 @@ static void amsDestroyTables(AMS ams, BT allocTable, /* AMSSegInit -- Init method for AMS segments */ -static Res AMSSegInit(Seg seg, Pool pool, Addr base, Size size, - Bool reservoirPermit, ArgList args) +static Res AMSSegInit(Seg seg, Pool pool, Addr base, Size size, ArgList args) { - SegClass super; AMSSeg amsseg; Res res; Arena arena; AMS ams; - AVERT(Seg, seg); - amsseg = Seg2AMSSeg(seg); + /* Initialize the superclass fields first via next-method call */ + res = NextMethod(Seg, AMSSeg, init)(seg, pool, base, size, args); + if (res != ResOK) + goto failNextMethod; + amsseg = CouldBeA(AMSSeg, seg); + AVERT(Pool, pool); ams = PoolAMS(pool); AVERT(AMS, ams); arena = PoolArena(pool); /* no useful checks for base and size */ - AVERT(Bool, reservoirPermit); - /* Initialize the superclass fields first via next-method call */ - super = SEG_SUPERCLASS(AMSSegClass); - res = super->init(seg, pool, base, size, reservoirPermit, args); - if (res != ResOK) - goto failNextMethod; - - amsseg->grains = size >> ams->grainShift; + amsseg->grains = PoolSizeGrains(pool, size); amsseg->freeGrains = amsseg->grains; - amsseg->oldGrains = (Count)0; + amsseg->bufferedGrains = (Count)0; amsseg->newGrains = (Count)0; + amsseg->oldGrains = (Count)0; amsseg->marksChanged = FALSE; /* */ amsseg->ambiguousFixes = FALSE; @@ -256,53 +266,41 @@ static Res AMSSegInit(Seg seg, Pool pool, Addr base, Size size, amsseg->allocTableInUse = FALSE; amsseg->firstFree = 0; amsseg->colourTablesInUse = FALSE; - amsseg->ams = ams; - RingInit(&amsseg->segRing); - RingAppend((ams->allocRing)(ams, SegRankSet(seg), size), - &amsseg->segRing); - + SetClassOfPoly(seg, CLASS(AMSSeg)); amsseg->sig = AMSSegSig; - AVERT(AMSSeg, amsseg); + AVERC(AMSSeg, amsseg); return ResOK; failCreateTables: - super->finish(seg); + NextMethod(Inst, AMSSeg, finish)(MustBeA(Inst, seg)); failNextMethod: + AVER(res != ResOK); return res; } /* AMSSegFinish -- Finish method for AMS segments */ -static void AMSSegFinish(Seg seg) +static void AMSSegFinish(Inst inst) { - SegClass super; - AMSSeg amsseg; - AMS ams; - Arena arena; + Seg seg = MustBeA(Seg, inst); + AMSSeg amsseg = MustBeA(AMSSeg, seg); + AMS ams = amsseg->ams; + Arena arena = PoolArena(AMSPool(ams)); - AVERT(Seg, seg); - amsseg = Seg2AMSSeg(seg); AVERT(AMSSeg, amsseg); - ams = amsseg->ams; - AVERT(AMS, ams); - arena = PoolArena(AMSPool(ams)); - AVER(SegBuffer(seg) == NULL); + AVER(!SegHasBuffer(seg)); /* keep the destructions in step with AMSSegInit failure cases */ amsDestroyTables(ams, amsseg->allocTable, amsseg->nongreyTable, amsseg->nonwhiteTable, arena, amsseg->grains); - RingRemove(&amsseg->segRing); - RingFinish(&amsseg->segRing); - amsseg->sig = SigInvalid; /* finish the superclass fields last */ - super = SEG_SUPERCLASS(AMSSegClass); - super->finish(seg); + NextMethod(Inst, AMSSeg, finish)(inst); } @@ -328,12 +326,11 @@ static void AMSSegFinish(Seg seg) */ static Res AMSSegMerge(Seg seg, Seg segHi, - Addr base, Addr mid, Addr limit, - Bool withReservoirPermit) + Addr base, Addr mid, Addr limit) { - SegClass super; Count loGrains, hiGrains, allGrains; AMSSeg amsseg, amssegHi; + Pool pool; Arena arena; AMS ams; BT allocTable, nongreyTable, nonwhiteTable; /* .table-names */ @@ -346,15 +343,16 @@ static Res AMSSegMerge(Seg seg, Seg segHi, AVERT(AMSSeg, amsseg); AVERT(AMSSeg, amssegHi); /* other parameters are checked by next-method */ - arena = PoolArena(SegPool(seg)); - ams = PoolAMS(SegPool(seg)); + pool = SegPool(seg); + arena = PoolArena(pool); + ams = PoolAMS(pool); loGrains = amsseg->grains; hiGrains = amssegHi->grains; allGrains = loGrains + hiGrains; /* checks for .grain-align */ - AVER(allGrains == AddrOffset(base, limit) >> ams->grainShift); + AVER(allGrains == PoolSizeGrains(pool, AddrOffset(base, limit))); /* checks for .empty */ AVER(amssegHi->freeGrains == hiGrains); AVER(!amssegHi->marksChanged); @@ -366,9 +364,7 @@ static Res AMSSegMerge(Seg seg, Seg segHi, goto failCreateTables; /* Merge the superclass fields via next-method call */ - super = SEG_SUPERCLASS(AMSSegClass); - res = super->merge(seg, segHi, base, mid, limit, - withReservoirPermit); + res = NextMethod(Seg, AMSSeg, merge)(seg, segHi, base, mid, limit); if (res != ResOK) goto failSuper; @@ -391,16 +387,15 @@ static Res AMSSegMerge(Seg seg, Seg segHi, amsseg->grains = allGrains; amsseg->freeGrains = amsseg->freeGrains + amssegHi->freeGrains; - amsseg->oldGrains = amsseg->oldGrains + amssegHi->oldGrains; + amsseg->bufferedGrains = amsseg->bufferedGrains + amssegHi->bufferedGrains; amsseg->newGrains = amsseg->newGrains + amssegHi->newGrains; + amsseg->oldGrains = amsseg->oldGrains + amssegHi->oldGrains; /* other fields in amsseg are unaffected */ - RingRemove(&amssegHi->segRing); - RingFinish(&amssegHi->segRing); amssegHi->sig = SigInvalid; AVERT(AMSSeg, amsseg); - PoolGenAccountForSegMerge(&ams->pgen); + PoolGenAccountForSegMerge(ams->pgen); return ResOK; failSuper: @@ -414,12 +409,11 @@ static Res AMSSegMerge(Seg seg, Seg segHi, static Res AMSSegSplit(Seg seg, Seg segHi, - Addr base, Addr mid, Addr limit, - Bool withReservoirPermit) + Addr base, Addr mid, Addr limit) { - SegClass super; Count loGrains, hiGrains, allGrains; AMSSeg amsseg, amssegHi; + Pool pool; Arena arena; AMS ams; BT allocTableLo, nongreyTableLo, nonwhiteTableLo; /* .table-names */ @@ -432,11 +426,12 @@ static Res AMSSegSplit(Seg seg, Seg segHi, amssegHi = Seg2AMSSeg(segHi); AVERT(AMSSeg, amsseg); /* other parameters are checked by next-method */ - arena = PoolArena(SegPool(seg)); - ams = PoolAMS(SegPool(seg)); + pool = SegPool(seg); + arena = PoolArena(pool); + ams = PoolAMS(pool); - loGrains = AMSGrains(ams, AddrOffset(base, mid)); - hiGrains = AMSGrains(ams, AddrOffset(mid, limit)); + loGrains = PoolSizeGrains(pool, AddrOffset(base, mid)); + hiGrains = PoolSizeGrains(pool, AddrOffset(mid, limit)); allGrains = loGrains + hiGrains; /* checks for .grain-align */ @@ -459,10 +454,8 @@ static Res AMSSegSplit(Seg seg, Seg segHi, if (res != ResOK) goto failCreateTablesHi; - /* Split the superclass fields via next-method call */ - super = SEG_SUPERCLASS(AMSSegClass); - res = super->split(seg, segHi, base, mid, limit, withReservoirPermit); + res = NextMethod(Seg, AMSSeg, split)(seg, segHi, base, mid, limit); if (res != ResOK) goto failSuper; @@ -487,8 +480,9 @@ static Res AMSSegSplit(Seg seg, Seg segHi, AVER(amsseg->freeGrains >= hiGrains); amsseg->freeGrains -= hiGrains; amssegHi->freeGrains = hiGrains; - amssegHi->oldGrains = (Count)0; + amssegHi->bufferedGrains = (Count)0; amssegHi->newGrains = (Count)0; + amssegHi->oldGrains = (Count)0; amssegHi->marksChanged = FALSE; /* */ amssegHi->ambiguousFixes = FALSE; @@ -497,16 +491,11 @@ static Res AMSSegSplit(Seg seg, Seg segHi, amssegHi->firstFree = 0; /* use colour tables if the segment is white */ amssegHi->colourTablesInUse = (SegWhite(segHi) != TraceSetEMPTY); - amssegHi->ams = ams; - RingInit(&amssegHi->segRing); - RingAppend((ams->allocRing)(ams, SegRankSet(segHi), SegSize(segHi)), - &amssegHi->segRing); - amssegHi->sig = AMSSegSig; AVERT(AMSSeg, amsseg); AVERT(AMSSeg, amssegHi); - PoolGenAccountForSegSplit(&ams->pgen); + PoolGenAccountForSegSplit(ams->pgen); return ResOK; failSuper: @@ -523,45 +512,44 @@ static Res AMSSegSplit(Seg seg, Seg segHi, /* AMSSegDescribe -- describe an AMS segment */ -#define WRITE_BUFFER_LIMIT(stream, seg, i, buffer, accessor, code) \ +#define WRITE_BUFFER_LIMIT(i, accessor, code) \ BEGIN \ - if ((buffer) != NULL \ - && (i) == AMS_ADDR_INDEX(seg, accessor(buffer))) { \ + if (hasBuffer && \ + (i) == PoolIndexOfAddr(SegBase(seg), SegPool(seg), accessor(buffer))) \ + { \ Res _res = WriteF(stream, 0, code, NULL); \ if (_res != ResOK) return _res; \ } \ END -static Res AMSSegDescribe(Seg seg, mps_lib_FILE *stream, Count depth) +static Res AMSSegDescribe(Inst inst, mps_lib_FILE *stream, Count depth) { + AMSSeg amsseg = CouldBeA(AMSSeg, inst); + Seg seg = CouldBeA(Seg, amsseg); Res res; - AMSSeg amsseg; - SegClass super; - Buffer buffer; /* the segment's buffer, if it has one */ + Buffer buffer; + Bool hasBuffer; Index i; - if (!TESTT(Seg, seg)) - return ResFAIL; + if (!TESTC(AMSSeg, amsseg)) + return ResPARAM; if (stream == NULL) - return ResFAIL; - amsseg = Seg2AMSSeg(seg); - if (!TESTT(AMSSeg, amsseg)) - return ResFAIL; + return ResPARAM; /* Describe the superclass fields first via next-method call */ - super = SEG_SUPERCLASS(AMSSegClass); - res = super->describe(seg, stream, depth); + res = NextMethod(Inst, AMSSeg, describe)(inst, stream, depth); if (res != ResOK) return res; - buffer = SegBuffer(seg); + hasBuffer = SegBuffer(&buffer, seg); - 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, + res = WriteF(stream, depth + 2, + "AMS $P\n", (WriteFP)amsseg->ams, + "grains $W\n", (WriteFW)amsseg->grains, + "freeGrains $W\n", (WriteFW)amsseg->freeGrains, + "buffferedGrains $W\n", (WriteFW)amsseg->bufferedGrains, + "newGrains $W\n", (WriteFW)amsseg->newGrains, + "oldGrains $W\n", (WriteFW)amsseg->oldGrains, NULL); if (res != ResOK) return res; @@ -596,9 +584,9 @@ static Res AMSSegDescribe(Seg seg, mps_lib_FILE *stream, Count depth) return res; } - WRITE_BUFFER_LIMIT(stream, seg, i, buffer, BufferBase, "["); - WRITE_BUFFER_LIMIT(stream, seg, i, buffer, BufferGetInit, "|"); - WRITE_BUFFER_LIMIT(stream, seg, i, buffer, BufferAlloc, ">"); + WRITE_BUFFER_LIMIT(i, BufferBase, "["); + WRITE_BUFFER_LIMIT(i, BufferGetInit, "|"); + WRITE_BUFFER_LIMIT(i, BufferAlloc, ">"); if (AMS_ALLOCED(seg, i)) { if (amsseg->colourTablesInUse) { @@ -618,8 +606,8 @@ static Res AMSSegDescribe(Seg seg, mps_lib_FILE *stream, Count depth) if (res != ResOK) return res; - WRITE_BUFFER_LIMIT(stream, seg, i+1, buffer, BufferScanLimit, "<"); - WRITE_BUFFER_LIMIT(stream, seg, i+1, buffer, BufferLimit, "]"); + WRITE_BUFFER_LIMIT(i+1, BufferScanLimit, "<"); + WRITE_BUFFER_LIMIT(i+1, BufferLimit, "]"); } return ResOK; @@ -628,27 +616,25 @@ static Res AMSSegDescribe(Seg seg, mps_lib_FILE *stream, Count depth) /* AMSSegClass -- Class definition for AMS segments */ -DEFINE_CLASS(AMSSegClass, class) +DEFINE_CLASS(Seg, AMSSeg, klass) { - INHERIT_CLASS(class, GCSegClass); - class->name = "AMSSEG"; - class->size = sizeof(AMSSegStruct); - class->init = AMSSegInit; - class->finish = AMSSegFinish; - 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) -{ - /* arguments checked in the caller */ - UNUSED(rankSet); UNUSED(size); - return &ams->segRing; + INHERIT_CLASS(klass, AMSSeg, MutatorSeg); + klass->instClassStruct.describe = AMSSegDescribe; + klass->instClassStruct.finish = AMSSegFinish; + klass->size = sizeof(AMSSegStruct); + klass->init = AMSSegInit; + klass->bufferFill = amsSegBufferFill; + klass->bufferEmpty = amsSegBufferEmpty; + klass->merge = AMSSegMerge; + klass->split = AMSSegSplit; + klass->whiten = amsSegWhiten; + klass->blacken = amsSegBlacken; + klass->scan = amsSegScan; + klass->fix = amsSegFix; + klass->fixEmergency = amsSegFix; + klass->reclaim = amsSegReclaim; + klass->walk = amsSegWalk; + AVERT(SegClass, klass); } @@ -682,7 +668,7 @@ static Res AMSSegSizePolicy(Size *sizeReturn, /* AMSSegCreate -- create a single AMSSeg */ static Res AMSSegCreate(Seg *segReturn, Pool pool, Size size, - RankSet rankSet, Bool withReservoirPermit) + RankSet rankSet) { Seg seg; AMS ams; @@ -694,7 +680,6 @@ static Res AMSSegCreate(Seg *segReturn, Pool pool, Size size, AVERT(Pool, pool); AVER(size > 0); AVERT(RankSet, rankSet); - AVERT(Bool, withReservoirPermit); ams = PoolAMS(pool); AVERT(AMS,ams); @@ -704,14 +689,14 @@ static Res AMSSegCreate(Seg *segReturn, Pool pool, Size size, if (res != ResOK) goto failSize; - res = PoolGenAlloc(&seg, &ams->pgen, (*ams->segClass)(), prefSize, - withReservoirPermit, argsNone); + res = PoolGenAlloc(&seg, ams->pgen, (*ams->segClass)(), prefSize, + argsNone); if (res != ResOK) { /* try to allocate one that's just large enough */ Size minSize = SizeArenaGrains(size, arena); if (minSize == prefSize) goto failSeg; - res = PoolGenAlloc(&seg, &ams->pgen, (*ams->segClass)(), prefSize, - withReservoirPermit, argsNone); + res = PoolGenAlloc(&seg, ams->pgen, (*ams->segClass)(), prefSize, + argsNone); if (res != ResOK) goto failSeg; } @@ -739,19 +724,22 @@ static Res AMSSegCreate(Seg *segReturn, Pool pool, Size size, static void AMSSegsDestroy(AMS ams) { + Pool pool = AMSPool(ams); Ring ring, node, next; /* for iterating over the segments */ ring = PoolSegRing(AMSPool(ams)); RING_FOR(node, ring, next) { Seg seg = SegOfPoolRing(node); AMSSeg amsseg = Seg2AMSSeg(seg); + AVER(!SegHasBuffer(seg)); AVERT(AMSSeg, amsseg); AVER(amsseg->ams == ams); + AVER(amsseg->bufferedGrains == 0); AMSSegFreeCheck(amsseg); - PoolGenFree(&ams->pgen, seg, - AMSGrainsSize(ams, amsseg->freeGrains), - AMSGrainsSize(ams, amsseg->oldGrains), - AMSGrainsSize(ams, amsseg->newGrains), + PoolGenFree(ams->pgen, seg, + PoolGrainsSize(pool, amsseg->freeGrains), + PoolGrainsSize(pool, amsseg->oldGrains), + PoolGrainsSize(pool, amsseg->newGrains), FALSE); } } @@ -787,79 +775,72 @@ static void AMSDebugVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) ARG_DEFINE_KEY(AMS_SUPPORT_AMBIGUOUS, Bool); -static Res AMSInit(Pool pool, ArgList args) +static Res AMSInit(Pool pool, Arena arena, PoolClass klass, ArgList args) { Res res; - Format format; Chain chain; Bool supportAmbiguous = AMS_SUPPORT_AMBIGUOUS_DEFAULT; unsigned gen = AMS_GEN_DEFAULT; ArgStruct arg; + AMS ams; - AVERT(Pool, pool); + AVER(pool != NULL); + AVERT(Arena, arena); AVERT(ArgList, args); + UNUSED(klass); /* used for debug pools only */ if (ArgPick(&arg, args, MPS_KEY_CHAIN)) chain = arg.val.chain; else { - chain = ArenaGlobals(PoolArena(pool))->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; - ArgRequire(&arg, args, MPS_KEY_FORMAT); - format = arg.val.format; if (ArgPick(&arg, args, MPS_KEY_AMS_SUPPORT_AMBIGUOUS)) supportAmbiguous = arg.val.b; - /* .ambiguous.noshare: If the pool is required to support ambiguous */ - /* references, the alloc and white tables cannot be shared. */ - res = AMSInitInternal(PoolAMS(pool), format, chain, gen, !supportAmbiguous); - if (res == ResOK) { - EVENT3(PoolInitAMS, pool, PoolArena(pool), format); - } - return res; -} - - -/* AMSInitInternal -- initialize an AMS pool, given the format and the chain */ - -Res AMSInitInternal(AMS ams, Format format, Chain chain, unsigned gen, - Bool shareAllocTable) -{ - Pool pool; - 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)); + AVER(chain->arena == arena); - pool->alignment = pool->format->alignment; - ams->grainShift = SizeLog2(PoolAlignment(pool)); - - res = PoolGenInit(&ams->pgen, ChainGen(chain, gen), pool); + res = NextMethod(Pool, AMSPool, init)(pool, arena, klass, args); if (res != ResOK) - return res; + goto failNextInit; + ams = CouldBeA(AMSPool, pool); - ams->shareAllocTable = shareAllocTable; - - RingInit(&ams->segRing); + /* Ensure a format was supplied in the argument list. */ + AVER(pool->format != NULL); + pool->alignment = pool->format->alignment; + pool->alignShift = SizeLog2(pool->alignment); + /* .ambiguous.noshare: If the pool is required to support ambiguous */ + /* references, the alloc and white tables cannot be shared. */ + ams->shareAllocTable = !supportAmbiguous; + ams->pgen = NULL; /* The next four might be overridden by a subclass. */ ams->segSize = AMSSegSizePolicy; - ams->allocRing = AMSPoolRing; ams->segsDestroy = AMSSegsDestroy; ams->segClass = AMSSegClassGet; + SetClassOfPoly(pool, CLASS(AMSPool)); ams->sig = AMSSig; - AVERT(AMS, ams); + AVERC(AMS, ams); + + res = PoolGenInit(&ams->pgenStruct, ChainGen(chain, gen), pool); + if (res != ResOK) + goto failGenInit; + ams->pgen = &ams->pgenStruct; + + EVENT3(PoolInitAMS, pool, PoolArena(pool), pool->format); + return ResOK; + +failGenInit: + NextMethod(Inst, AMSPool, finish)(MustBeA(Inst, pool)); +failNextInit: + AVER(res != ResOK); + return res; } @@ -868,72 +849,98 @@ Res AMSInitInternal(AMS ams, Format format, Chain chain, unsigned gen, * Destroys all the segs in the pool. Can't invalidate the AMS until * we've destroyed all the segments, as it may be checked. */ -void AMSFinish(Pool pool) +void AMSFinish(Inst inst) { - AMS ams; + Pool pool = MustBeA(AbstractPool, inst); + AMS ams = MustBeA(AMSPool, pool); - AVERT(Pool, pool); - ams = PoolAMS(pool); AVERT(AMS, ams); - (ams->segsDestroy)(ams); + ams->segsDestroy(ams); /* can't invalidate the AMS until we've destroyed all the segs */ ams->sig = SigInvalid; - RingFinish(&ams->segRing); - PoolGenFinish(&ams->pgen); + PoolGenFinish(ams->pgen); + ams->pgen = NULL; + + NextMethod(Inst, AMSPool, finish)(inst); } -/* amsSegAlloc -- try to allocate an area in the given segment - * - * Tries to find an area of at least the given size. If successful, - * returns its base and limit grain indices. - */ -static Bool amsSegAlloc(Index *baseReturn, Index *limitReturn, - Seg seg, Size size) +/* amsSegBufferFill -- try filling buffer from segment */ + +static Bool amsSegBufferFill(Addr *baseReturn, Addr *limitReturn, + Seg seg, Size size, RankSet rankSet) { - AMS ams; - AMSSeg amsseg; - Size grains; - Bool canAlloc; /* can we allocate in this segment? */ - Index base, limit; + Index baseIndex, limitIndex; + AMSSeg amsseg = MustBeA(AMSSeg, seg); + Pool pool = SegPool(seg); + Count requestedGrains, segGrains, allocatedGrains; + Addr segBase, base, limit; AVER(baseReturn != NULL); AVER(limitReturn != NULL); - /* seg has already been checked, in AMSBufferFill. */ - amsseg = Seg2AMSSeg(seg); - - ams = amsseg->ams; - AVERT(AMS, ams); - + AVER(SizeIsAligned(size, PoolAlignment(pool))); AVER(size > 0); - AVER(SizeIsAligned(size, PoolAlignment(AMSPool(ams)))); + AVERT(RankSet, rankSet); - grains = AMSGrains(ams, size); - AVER(grains > 0); - if (grains > amsseg->grains) + requestedGrains = PoolSizeGrains(pool, size); + if (amsseg->freeGrains < requestedGrains) + /* Not enough space to satisfy the request. */ return FALSE; - if (amsseg->allocTableInUse) { - canAlloc = BTFindLongResRange(&base, &limit, amsseg->allocTable, - 0, amsseg->grains, grains); - if (!canAlloc) - return FALSE; - BTSetRange(amsseg->allocTable, base, limit); - } else { - if (amsseg->firstFree > amsseg->grains - grains) - return FALSE; - base = amsseg->firstFree; - limit = amsseg->grains; - amsseg->firstFree = limit; + if (SegHasBuffer(seg)) + /* Don't bother trying to allocate from a buffered segment */ + return FALSE; + + if (RefSetUnion(SegWhite(seg), SegGrey(seg)) != TraceSetEMPTY) + /* Can't use a white or grey segment, see design.mps.poolams.fill.colour */ + return FALSE; + + if (rankSet != SegRankSet(seg)) + /* Can't satisfy required rank set. */ + return FALSE; + + segGrains = PoolSizeGrains(pool, SegSize(seg)); + if (amsseg->freeGrains == segGrains) { + /* Whole segment is free: no need for a search. */ + baseIndex = 0; + limitIndex = segGrains; + goto found; } /* We don't place buffers on white segments, so no need to adjust colour. */ AVER(!amsseg->colourTablesInUse); - AVER(amsseg->freeGrains >= limit - base); - amsseg->freeGrains -= limit - base; - amsseg->newGrains += limit - base; + if (amsseg->allocTableInUse) { + if (!BTFindLongResRange(&baseIndex, &limitIndex, amsseg->allocTable, + 0, segGrains, requestedGrains)) + return FALSE; + } else { + if (amsseg->firstFree > segGrains - requestedGrains) + return FALSE; + baseIndex = amsseg->firstFree; + limitIndex = segGrains; + } + +found: + AVER(baseIndex < limitIndex); + if (amsseg->allocTableInUse) { + BTSetRange(amsseg->allocTable, baseIndex, limitIndex); + } else { + amsseg->firstFree = limitIndex; + } + allocatedGrains = limitIndex - baseIndex; + AVER(requestedGrains <= allocatedGrains); + AVER(amsseg->freeGrains >= allocatedGrains); + amsseg->freeGrains -= allocatedGrains; + amsseg->bufferedGrains += allocatedGrains; + + segBase = SegBase(seg); + base = PoolAddrOfIndex(segBase, pool, baseIndex); + limit = PoolAddrOfIndex(segBase, pool, limitIndex); + PoolGenAccountForFill(PoolSegPoolGen(pool, seg), AddrOffset(base, limit)); + DebugPoolFreeCheck(pool, base, limit); + *baseReturn = base; *limitReturn = limit; return TRUE; @@ -946,158 +953,137 @@ static Bool amsSegAlloc(Index *baseReturn, Index *limitReturn, * . */ static Res AMSBufferFill(Addr *baseReturn, Addr *limitReturn, - Pool pool, Buffer buffer, Size size, - Bool withReservoirPermit) + Pool pool, Buffer buffer, Size size) { Res res; - AMS ams; - Seg seg; - Ring node, ring, nextNode; /* for iterating over the segments */ - Index base = 0, limit = 0; /* suppress "may be used uninitialized" */ - Addr baseAddr, limitAddr; + Ring node, nextNode; RankSet rankSet; - Bool b; /* the return value of amsSegAlloc */ - Size allocatedSize; + Seg seg; + Bool b; AVER(baseReturn != NULL); AVER(limitReturn != NULL); - AVERT(Pool, pool); - ams = PoolAMS(pool); - AVERT(AMS, ams); - AVERT(Buffer, buffer); + AVERC(Buffer, buffer); + AVER(BufferIsReset(buffer)); AVER(size > 0); AVER(SizeIsAligned(size, PoolAlignment(pool))); - AVERT(Bool, withReservoirPermit); /* Check that we're not in the grey mutator phase (see */ /* ). */ AVER(PoolArena(pool)->busyTraces == PoolArena(pool)->flippedTraces); - rankSet = BufferRankSet(buffer); - ring = (ams->allocRing)(ams, rankSet, size); /* */ - RING_FOR(node, ring, nextNode) { - AMSSeg amsseg = RING_ELT(AMSSeg, segRing, node); - AVERT_CRITICAL(AMSSeg, amsseg); - if (amsseg->freeGrains >= AMSGrains(ams, size)) { - seg = AMSSeg2Seg(amsseg); - - 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) - { - b = amsSegAlloc(&base, &limit, seg, size); - if (b) - goto found; - } - } + rankSet = BufferRankSet(buffer); + RING_FOR(node, &pool->segRing, nextNode) { + seg = SegOfPoolRing(node); + if (SegBufferFill(baseReturn, limitReturn, seg, size, rankSet)) + return ResOK; } - /* No suitable segment found; make a new one. */ - res = AMSSegCreate(&seg, pool, size, rankSet, - withReservoirPermit); + /* No segment had enough space, so make a new one. */ + res = AMSSegCreate(&seg, pool, size, BufferRankSet(buffer)); if (res != ResOK) return res; - b = amsSegAlloc(&base, &limit, seg, size); - -found: + b = SegBufferFill(baseReturn, limitReturn, seg, size, rankSet); AVER(b); - baseAddr = AMS_INDEX_ADDR(seg, base); limitAddr = AMS_INDEX_ADDR(seg, limit); - DebugPoolFreeCheck(pool, baseAddr, limitAddr); - allocatedSize = AddrOffset(baseAddr, limitAddr); - - PoolGenAccountForFill(&ams->pgen, allocatedSize, FALSE); - *baseReturn = baseAddr; - *limitReturn = limitAddr; return ResOK; } -/* AMSBufferEmpty -- the pool class buffer empty method +/* amsSegBufferEmpty -- empty buffer to segment * * Frees the unused part of the buffer. The colour of the area doesn't * need to be changed. See . */ -static void AMSBufferEmpty(Pool pool, Buffer buffer, Addr init, Addr limit) +static void amsSegBufferEmpty(Seg seg, Buffer buffer) { - AMS ams; + AMSSeg amsseg = MustBeA(AMSSeg, seg); + Pool pool = SegPool(seg); + Addr segBase, bufferBase, init, limit; Index initIndex, limitIndex; - Seg seg; - AMSSeg amsseg; - Size size; + Count usedGrains, unusedGrains; - AVERT(Pool, pool); - ams = PoolAMS(pool); - AVERT(AMS, ams); - AVERT(Buffer,buffer); - AVER(BufferIsReady(buffer)); - seg = BufferSeg(buffer); AVERT(Seg, seg); + AVERT(Buffer, buffer); + segBase = SegBase(seg); + bufferBase = BufferBase(buffer); + init = BufferGetInit(buffer); + limit = BufferLimit(buffer); + AVER(segBase <= bufferBase); + AVER(bufferBase <= init); AVER(init <= limit); - AVER(AddrIsAligned(init, PoolAlignment(pool))); - AVER(AddrIsAligned(limit, PoolAlignment(pool))); + AVER(limit <= SegLimit(seg)); - amsseg = Seg2AMSSeg(seg); - AVERT(AMSSeg, amsseg); + initIndex = PoolIndexOfAddr(segBase, pool, init); + limitIndex = PoolIndexOfAddr(segBase, pool, limit); - if (init == limit) - return; + if (initIndex < limitIndex) { + AMS ams = MustBeA(AMSPool, pool); - /* Tripped allocations might have scribbled on it, need to splat again. */ - DebugPoolFreeSplat(pool, init, limit); + /* Tripped allocations might have scribbled on it, need to splat again. */ + DebugPoolFreeSplat(pool, init, limit); - initIndex = AMS_ADDR_INDEX(seg, init); - limitIndex = AMS_ADDR_INDEX(seg, limit); - - if (amsseg->allocTableInUse) { - /* check that it's allocated */ - AVER(BTIsSetRange(amsseg->allocTable, initIndex, limitIndex)); - BTResRange(amsseg->allocTable, initIndex, limitIndex); - } else { - /* check that it's allocated */ - AVER(limitIndex <= amsseg->firstFree); - if (limitIndex == amsseg->firstFree) /* is it at the end? */ { - amsseg->firstFree = initIndex; - } 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) - BTResRange(amsseg->allocTable, amsseg->firstFree, amsseg->grains); + if (amsseg->allocTableInUse) { + /* check that it's allocated */ + AVER(BTIsSetRange(amsseg->allocTable, initIndex, limitIndex)); BTResRange(amsseg->allocTable, initIndex, limitIndex); + } else { + /* check that it's allocated */ + AVER(limitIndex <= amsseg->firstFree); + if (limitIndex == amsseg->firstFree) /* is it at the end? */ { + amsseg->firstFree = initIndex; + } 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 amsSegWhiten); 2. the segment has not yet been reclaimed + * (because colour tables are turned off in amsSegReclaim); 3. the + * unused portion of the buffer is black (see amsSegWhiten). So we + * need to whiten the unused portion of the buffer. The allocTable + * will be turned back on (if necessary) in amsSegReclaim, 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) + BTResRange(amsseg->allocTable, amsseg->firstFree, amsseg->grains); + BTResRange(amsseg->allocTable, initIndex, limitIndex); + } } + + if (amsseg->colourTablesInUse) + AMS_RANGE_WHITEN(seg, initIndex, limitIndex); } - if (amsseg->colourTablesInUse) - AMS_RANGE_WHITEN(seg, initIndex, limitIndex); + unusedGrains = limitIndex - initIndex; + AVER(unusedGrains <= amsseg->bufferedGrains); + usedGrains = amsseg->bufferedGrains - unusedGrains; + amsseg->freeGrains += unusedGrains; + amsseg->bufferedGrains = 0; + amsseg->newGrains += usedGrains; - 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); - PoolGenAccountForEmpty(&ams->pgen, size, FALSE); + PoolGenAccountForEmpty(PoolSegPoolGen(pool, seg), + PoolGrainsSize(pool, usedGrains), + PoolGrainsSize(pool, unusedGrains), FALSE); } -/* amsRangeWhiten -- Condemn a part of an AMS segment +/* amsSegPoolGen -- get pool generation for an AMS segment */ + +static PoolGen amsSegPoolGen(Pool pool, Seg seg) +{ + AMS ams = MustBeA(AMSPool, pool); + AVERT(Seg, seg); + return ams->pgen; +} + + +/* amsSegRangeWhiten -- Condemn a part of an AMS segment * Allow calling it with base = limit, to simplify the callers. */ -static void amsRangeWhiten(Seg seg, Index base, Index limit) +static void amsSegRangeWhiten(Seg seg, Index base, Index limit) { if (base != limit) { AMSSeg amsseg = Seg2AMSSeg(seg); @@ -1110,24 +1096,17 @@ static void amsRangeWhiten(Seg seg, Index base, Index limit) } -/* AMSWhiten -- the pool class segment condemning method */ +/* amsSegWhiten -- the pool class segment condemning method */ -static Res AMSWhiten(Pool pool, Trace trace, Seg seg) +static Res amsSegWhiten(Seg seg, Trace trace) { - AMS ams; - AMSSeg amsseg; Buffer buffer; /* the seg's buffer, if it has one */ - Count uncondemned; - - AVERT(Pool, pool); - ams = PoolAMS(pool); - AVERT(AMS, ams); + Count agedGrains, uncondemnedGrains; + AMSSeg amsseg = MustBeA(AMSSeg, seg); + Pool pool = SegPool(seg); + PoolGen pgen = PoolSegPoolGen(pool, seg); AVERT(Trace, trace); - AVERT(Seg, seg); - - amsseg = Seg2AMSSeg(seg); - AVERT(AMSSeg, amsseg); /* */ AVER(SegWhite(seg) == TraceSetEMPTY); @@ -1144,7 +1123,7 @@ static Res AMSWhiten(Pool pool, Trace trace, Seg seg) } /* Start using allocTable as the white table, if so configured. */ - if (ams->shareAllocTable) { + if (amsseg->ams->shareAllocTable) { if (amsseg->allocTableInUse) { /* During the collection, it can't use allocTable for AMS_ALLOCED, so */ /* make it use firstFree. */ @@ -1156,32 +1135,40 @@ static Res AMSWhiten(Pool pool, Trace trace, Seg seg) amsseg->allocTableInUse = TRUE; } - buffer = SegBuffer(seg); - if (buffer != NULL) { /* */ + if (SegBuffer(&buffer, seg)) { /* */ Index scanLimitIndex, limitIndex; - scanLimitIndex = AMS_ADDR_INDEX(seg, BufferScanLimit(buffer)); - limitIndex = AMS_ADDR_INDEX(seg, BufferLimit(buffer)); + scanLimitIndex = PoolIndexOfAddr(SegBase(seg), pool, BufferScanLimit(buffer)); + limitIndex = PoolIndexOfAddr(SegBase(seg), pool, BufferLimit(buffer)); - amsRangeWhiten(seg, 0, scanLimitIndex); + amsSegRangeWhiten(seg, 0, scanLimitIndex); if (scanLimitIndex < limitIndex) AMS_RANGE_BLACKEN(seg, scanLimitIndex, limitIndex); - amsRangeWhiten(seg, limitIndex, amsseg->grains); + amsSegRangeWhiten(seg, limitIndex, amsseg->grains); /* We didn't condemn the buffer, subtract it from the count. */ - uncondemned = limitIndex - scanLimitIndex; + uncondemnedGrains = limitIndex - scanLimitIndex; } else { /* condemn whole seg */ - amsRangeWhiten(seg, 0, amsseg->grains); - uncondemned = (Count)0; + amsSegRangeWhiten(seg, 0, amsseg->grains); + uncondemnedGrains = (Count)0; } - /* 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; + /* The unused part of the buffer remains buffered: the rest becomes old. */ + AVER(amsseg->bufferedGrains >= uncondemnedGrains); + agedGrains = amsseg->bufferedGrains - uncondemnedGrains; + PoolGenAccountForAge(pgen, PoolGrainsSize(pool, agedGrains), + PoolGrainsSize(pool, amsseg->newGrains), FALSE); + amsseg->oldGrains += agedGrains + amsseg->newGrains; + amsseg->bufferedGrains = uncondemnedGrains; + amsseg->newGrains = 0; amsseg->marksChanged = FALSE; /* */ amsseg->ambiguousFixes = FALSE; - trace->condemned += AMSGrainsSize(ams, amsseg->oldGrains); - SegSetWhite(seg, TraceSetAdd(SegWhite(seg), trace)); + if (amsseg->oldGrains > 0) { + GenDescCondemned(pgen->gen, trace, + PoolGrainsSize(pool, amsseg->oldGrains)); + SegSetWhite(seg, TraceSetAdd(SegWhite(seg), trace)); + } else { + amsseg->colourTablesInUse = FALSE; + } return ResOK; } @@ -1200,22 +1187,23 @@ typedef Res (*AMSObjectFunction)( ((f) != NULL) /* that's the best we can do */ -/* amsIterate -- applies a function to each object in a segment +/* semSegIterate -- applies a function to each object in a segment * - * amsIterate(seg, f, closure) applies f to all the objects in the + * semSegIterate(seg, f, closure) applies f to all the objects in the * segment. It skips the buffer, if any (from BufferScanLimit to * BufferLimit). */ -static Res amsIterate(Seg seg, AMSObjectFunction f, void *closure) +static Res semSegIterate(Seg seg, AMSObjectFunction f, void *closure) { Res res; - AMS ams; + Pool pool; AMSSeg amsseg; Format format; Align alignment; Index i; Addr p, next, limit; Buffer buffer; + Bool hasBuffer; AVERT(Seg, seg); AVERT(AMSObjectFunction, f); @@ -1223,32 +1211,31 @@ static Res amsIterate(Seg seg, AMSObjectFunction f, void *closure) amsseg = Seg2AMSSeg(seg); AVERT(AMSSeg, amsseg); - ams = amsseg->ams; - AVERT(AMS, ams); - format = AMSPool(ams)->format; + pool = SegPool(seg); + AVERT(Pool, pool); + format = pool->format; AVERT(Format, format); - alignment = PoolAlignment(AMSPool(ams)); + alignment = PoolAlignment(pool); /* If we're using the alloc table as a white table, we can't use it to */ /* determine where there are objects. */ - AVER(!(ams->shareAllocTable && amsseg->colourTablesInUse)); + AVER(!amsseg->ams->shareAllocTable || !amsseg->colourTablesInUse); p = SegBase(seg); limit = SegLimit(seg); - buffer = SegBuffer(seg); + hasBuffer = SegBuffer(&buffer, seg); while (p < limit) { /* loop over the objects in the segment */ - if (buffer != NULL - && p == BufferScanLimit(buffer) && p != BufferLimit(buffer)) { + if (hasBuffer && p == BufferScanLimit(buffer) && p != BufferLimit(buffer)) { /* skip buffer */ next = BufferLimit(buffer); AVER(AddrIsAligned(next, alignment)); } else { - AVER((buffer == NULL) + AVER(!hasBuffer || (p < BufferScanLimit(buffer)) || (p >= BufferLimit(buffer))); /* not in the buffer */ - i = AMS_ADDR_INDEX(seg, p); + i = PoolIndexOfAddr(SegBase(seg), pool, p); if (!AMS_ALLOCED(seg, i)) { /* no object here */ if (amsseg->allocTableInUse) { Index dummy, nextIndex; @@ -1259,7 +1246,7 @@ static Res amsIterate(Seg seg, AMSObjectFunction f, void *closure) i, amsseg->grains, 1); AVER(more); AVER(dummy == i); - next = AMS_INDEX_ADDR(seg, nextIndex); + next = PoolAddrOfIndex(SegBase(seg), pool, nextIndex); } else { /* If there's no allocTable, this is the free block at the end. */ next = limit; @@ -1287,7 +1274,7 @@ static Res amsIterate(Seg seg, AMSObjectFunction f, void *closure) /* amsScanObject -- scan a single object * - * This is the object function passed to amsIterate by AMSScan. */ + * This is the object function passed to semSegIterate by amsSegScan. */ struct amsScanClosureStruct { ScanState ss; @@ -1304,7 +1291,7 @@ static Res amsScanObject(Seg seg, Index i, Addr p, Addr next, void *clos) Res res; amsseg = Seg2AMSSeg(seg); - /* seg & amsseg have already been checked, in amsIterate. */ + /* seg & amsseg have already been checked, in semSegIterate. */ AVER(i < amsseg->grains); AVER(p != 0); AVER(p < next); @@ -1325,7 +1312,7 @@ static Res amsScanObject(Seg seg, Index i, Addr p, Addr next, void *clos) if (res != ResOK) return res; if (!closure->scanAllObjects) { - Index j = AMS_ADDR_INDEX(seg, next); + Index j = PoolIndexOfAddr(SegBase(seg), SegPool(seg), next); AVER(!AMS_IS_INVALID_COLOUR(seg, i)); AMS_GREY_BLACKEN(seg, i); if (i+1 < j) @@ -1337,29 +1324,23 @@ static Res amsScanObject(Seg seg, Index i, Addr p, Addr next, void *clos) } -/* AMSScan -- the pool class segment scanning method +/* amsSegScan -- the segment scanning method * * See */ -Res AMSScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg) +static Res amsSegScan(Bool *totalReturn, Seg seg, ScanState ss) { Res res; - AMS ams; - Arena arena; - AMSSeg amsseg; + AMSSeg amsseg = MustBeA(AMSSeg, seg); + Pool pool = SegPool(seg); + AMS ams = MustBeA(AMSPool, pool); + Arena arena = PoolArena(pool); struct amsScanClosureStruct closureStruct; Format format; Align alignment; AVER(totalReturn != NULL); AVERT(ScanState, ss); - AVERT(Pool, pool); - ams = PoolAMS(pool); - AVERT(AMS, ams); - arena = PoolArena(pool); - AVERT(Seg, seg); - amsseg = Seg2AMSSeg(seg); - AVERT(AMSSeg, amsseg); /* Check that we're not in the grey mutator phase (see */ /* ). */ @@ -1371,7 +1352,7 @@ Res AMSScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg) /* @@@@ This isn't quite right for multiple traces. */ if (closureStruct.scanAllObjects) { /* The whole seg (except the buffer) is grey for some trace. */ - res = amsIterate(seg, amsScanObject, &closureStruct); + res = semSegIterate(seg, amsScanObject, &closureStruct); if (res != ResOK) { *totalReturn = FALSE; return res; @@ -1387,7 +1368,7 @@ Res AMSScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg) amsseg->marksChanged = FALSE; /* */ /* */ if (amsseg->ambiguousFixes) { - res = amsIterate(seg, amsScanObject, &closureStruct); + res = semSegIterate(seg, amsScanObject, &closureStruct); if (res != ResOK) { /* */ amsseg->marksChanged = TRUE; @@ -1402,7 +1383,7 @@ Res AMSScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg) && AMSFindGrey(&i, &j, seg, j, amsseg->grains)) { Addr clientP, clientNext; AVER(!AMS_IS_INVALID_COLOUR(seg, i)); - p = AMS_INDEX_ADDR(seg, i); + p = PoolAddrOfIndex(SegBase(seg), pool, i); clientP = AddrAdd(p, format->headerSize); if (format->skip != NULL) { clientNext = (*format->skip)(clientP); @@ -1411,7 +1392,7 @@ Res AMSScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg) clientNext = AddrAdd(clientP, alignment); next = AddrAdd(p, alignment); } - j = AMS_ADDR_INDEX(seg, next); + j = PoolIndexOfAddr(SegBase(seg), pool, next); res = FormatScan(format, ss, clientP, clientNext); if (res != ResOK) { /* */ @@ -1435,24 +1416,22 @@ Res AMSScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg) } -/* AMSFix -- the pool class fixing method */ +/* amsSegFix -- the segment fixing method */ -static Res AMSFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) +static Res amsSegFix(Seg seg, ScanState ss, Ref *refIO) { - AMSSeg amsseg; + AMSSeg amsseg = MustBeA_CRITICAL(AMSSeg, seg); + Pool pool = SegPool(seg); Index i; /* the index of the fixed grain */ Addr base; Ref clientRef; Format format; - AVERT_CRITICAL(Pool, pool); - AVER_CRITICAL(TESTT(AMS, PoolAMS(pool))); AVERT_CRITICAL(ScanState, ss); - AVERT_CRITICAL(Seg, seg); AVER_CRITICAL(refIO != NULL); format = pool->format; - AVERT(Format, format); + AVERT_CRITICAL(Format, format); amsseg = Seg2AMSSeg(seg); AVERT_CRITICAL(AMSSeg, amsseg); @@ -1467,20 +1446,30 @@ static Res AMSFix(Pool pool, ScanState ss, Seg seg, Ref *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. */ + + /* Not a real reference if out of bounds. This can happen if an + ambiguous reference is closer to the base of the segment than the + header size. */ if (base < SegBase(seg)) { - AVER_CRITICAL(ss->rank == RankAMBIG); + AVER(ss->rank == RankAMBIG); return ResOK; } - i = AMS_ADDR_INDEX(seg, base); + /* Not a real reference if unaligned. */ + if (!AddrIsAligned(base, PoolAlignment(pool))) { + AVER(ss->rank == RankAMBIG); + return ResOK; + } + + i = PoolIndexOfAddr(SegBase(seg), pool, base); AVER_CRITICAL(i < amsseg->grains); AVER_CRITICAL(!AMS_IS_INVALID_COLOUR(seg, i)); - ss->wasMarked = TRUE; + /* Not a real reference if unallocated. */ + if (!AMS_ALLOCED(seg, i)) { + AVER(ss->rank == RankAMBIG); + return ResOK; + } switch (ss->rank) { case RankAMBIG: @@ -1488,24 +1477,17 @@ static Res AMSFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) /* In this state, the pool doesn't support ambiguous references (see */ /* .ambiguous.noshare), so this is not a reference. */ break; - /* not a real pointer if not aligned or not allocated */ - if (!AddrIsAligned(base, PoolAlignment(pool)) - || !AMS_ALLOCED(seg, i)) { - break; - } amsseg->ambiguousFixes = TRUE; /* falls through */ case RankEXACT: case RankFINAL: case RankWEAK: - AVER_CRITICAL(AddrIsAligned(base, PoolAlignment(pool))); - AVER_CRITICAL(AMS_ALLOCED(seg, i)); if (AMS_IS_WHITE(seg, i)) { - ss->wasMarked = FALSE; + ss->wasMarked = FALSE; /* */ if (ss->rank == RankWEAK) { /* then splat the reference */ *refIO = (Ref)0; } else { - ++ss->preservedInPlaceCount; /* Size updated on reclaim */ + STATISTIC(++ss->preservedInPlaceCount); /* Size updated on reclaim */ if (SegRankSet(seg) == RankSetEMPTY && ss->rank != RankAMBIG) { /* */ Addr clientNext, next; @@ -1516,7 +1498,7 @@ static Res AMSFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) next = AddrSub(clientNext, format->headerSize); /* Part of the object might be grey, because of ambiguous */ /* fixes, but that's OK, because scan will ignore that. */ - AMS_RANGE_WHITE_BLACKEN(seg, i, AMS_ADDR_INDEX(seg, next)); + AMS_RANGE_WHITE_BLACKEN(seg, i, PoolIndexOfAddr(SegBase(seg), pool, next)); } else { /* turn it grey */ AMS_WHITE_GREYEN(seg, i); SegSetGrey(seg, TraceSetUnion(SegGrey(seg), ss->traces)); @@ -1534,18 +1516,17 @@ static Res AMSFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) } -/* AMSBlacken -- the pool class blackening method +/* amsSegBlacken -- the segment blackening method * * Turn all grey objects black. */ - -static Res amsBlackenObject(Seg seg, Index i, Addr p, Addr next, void *clos) +static Res amsSegBlackenObject(Seg seg, Index i, Addr p, Addr next, void *clos) { UNUSED(p); - AVER(clos == NULL); + AVER(clos == UNUSED_POINTER); /* Do what amsScanObject does, minus the scanning. */ if (AMS_IS_GREY(seg, i)) { - Index j = AMS_ADDR_INDEX(seg, next); + Index j = PoolIndexOfAddr(SegBase(seg), SegPool(seg), next); AVER(!AMS_IS_INVALID_COLOUR(seg, i)); AMS_GREY_BLACKEN(seg, i); if (i+1 < j) @@ -1554,15 +1535,10 @@ static Res amsBlackenObject(Seg seg, Index i, Addr p, Addr next, void *clos) return ResOK; } - -static void AMSBlacken(Pool pool, TraceSet traceSet, Seg seg) +static void amsSegBlacken(Seg seg, TraceSet traceSet) { - AMS ams; Res res; - AVERT(Pool, pool); - ams = PoolAMS(pool); - AVERT(AMS, ams); AVERT(TraceSet, traceSet); AVERT(Seg, seg); @@ -1572,41 +1548,39 @@ static void AMSBlacken(Pool pool, TraceSet traceSet, Seg seg) AVERT(AMSSeg, amsseg); AVER(amsseg->marksChanged); /* there must be something grey */ amsseg->marksChanged = FALSE; - res = amsIterate(seg, amsBlackenObject, NULL); + res = semSegIterate(seg, amsSegBlackenObject, UNUSED_POINTER); AVER(res == ResOK); } } -/* AMSReclaim -- the pool class reclamation method */ +/* amsSegReclaim -- the segment reclamation method */ -static void AMSReclaim(Pool pool, Trace trace, Seg seg) +static void amsSegReclaim(Seg seg, Trace trace) { - AMS ams; - AMSSeg amsseg; + AMSSeg amsseg = MustBeA(AMSSeg, seg); + Pool pool = SegPool(seg); + PoolGen pgen = PoolSegPoolGen(pool, seg); Count nowFree, grains, reclaimedGrains; + Size preservedInPlaceSize; PoolDebugMixin debug; - AVERT(Pool, pool); - ams = PoolAMS(pool); - AVERT(AMS, ams); AVERT(Trace, trace); - AVERT(Seg, seg); - amsseg = Seg2AMSSeg(seg); /* It's a white seg, so it must have colour tables. */ AVER(amsseg->colourTablesInUse); AVER(!amsseg->marksChanged); /* there must be nothing grey */ grains = amsseg->grains; /* Loop over all white blocks and splat them, if it's a debug class. */ - debug = ((pool)->class->debugMixin)(pool); + debug = Method(Pool, pool, debugMixin)(pool); if (debug != NULL) { Index i, j = 0; while(j < grains && AMS_FIND_WHITE_RANGE(&i, &j, seg, j, grains)) { AVER(!AMS_IS_INVALID_COLOUR(seg, i)); - DebugPoolFreeSplat(pool, AMS_INDEX_ADDR(seg, i), AMS_INDEX_ADDR(seg, j)); + DebugPoolFreeSplat(pool, PoolAddrOfIndex(SegBase(seg), pool, i), + PoolAddrOfIndex(SegBase(seg), pool, j)); ++j; /* we know next grain is not white */ } } @@ -1620,7 +1594,7 @@ static void AMSReclaim(Pool pool, Trace trace, Seg seg) || BTIsResRange(amsseg->nonwhiteTable, amsseg->firstFree, grains)); } else { - if (ams->shareAllocTable) { + if (amsseg->ams->shareAllocTable) { /* Stop using allocTable as the white table. */ amsseg->allocTableInUse = TRUE; } else { @@ -1633,22 +1607,76 @@ static void AMSReclaim(Pool pool, Trace trace, Seg seg) AVER(amsseg->oldGrains >= reclaimedGrains); amsseg->oldGrains -= reclaimedGrains; amsseg->freeGrains += reclaimedGrains; - PoolGenAccountForReclaim(&ams->pgen, AMSGrainsSize(ams, reclaimedGrains), FALSE); - trace->reclaimSize += AMSGrainsSize(ams, reclaimedGrains); + PoolGenAccountForReclaim(pgen, PoolGrainsSize(pool, reclaimedGrains), FALSE); + STATISTIC(trace->reclaimSize += PoolGrainsSize(pool, reclaimedGrains)); /* preservedInPlaceCount is updated on fix */ - trace->preservedInPlaceSize += AMSGrainsSize(ams, amsseg->oldGrains); + preservedInPlaceSize = PoolGrainsSize(pool, amsseg->oldGrains); + GenDescSurvived(pgen->gen, trace, 0, preservedInPlaceSize); /* Ensure consistency of segment even if are just about to free it */ amsseg->colourTablesInUse = FALSE; SegSetWhite(seg, TraceSetDel(SegWhite(seg), trace)); - if (amsseg->freeGrains == grains && SegBuffer(seg) == NULL) + if (amsseg->freeGrains == grains && !SegHasBuffer(seg)) { /* No survivors */ - PoolGenFree(&ams->pgen, seg, - AMSGrainsSize(ams, amsseg->freeGrains), - AMSGrainsSize(ams, amsseg->oldGrains), - AMSGrainsSize(ams, amsseg->newGrains), + AVER(amsseg->bufferedGrains == 0); + PoolGenFree(pgen, seg, + PoolGrainsSize(pool, amsseg->freeGrains), + PoolGrainsSize(pool, amsseg->oldGrains), + PoolGrainsSize(pool, amsseg->newGrains), FALSE); + } +} + + +/* amsSegWalk -- walk formatted objects in AMC segment */ + +static void amsSegWalk(Seg seg, Format format, FormattedObjectsVisitor f, + void *p, size_t s) +{ + AMSSeg amsseg = MustBeA(AMSSeg, seg); + Pool pool = SegPool(seg); + Addr object, base, limit; + + AVERT(Format, format); + AVER(FUNCHECK(f)); + /* p and s are arbitrary closures and can't be checked */ + + base = SegBase(seg); + object = base; + limit = SegLimit(seg); + + while (object < limit) { + /* object is a slight misnomer because it might point to a free grain */ + Addr next; + Index i; + Buffer buffer; + + if (SegBuffer(&buffer, seg)) { + if (object == BufferScanLimit(buffer) + && BufferScanLimit(buffer) != BufferLimit(buffer)) { + /* skip over buffered area */ + object = BufferLimit(buffer); + continue; + } + /* since we skip over the buffered area we are always */ + /* either before the buffer, or after it, never in it */ + AVER(object < BufferGetInit(buffer) || BufferLimit(buffer) <= object); + } + i = PoolIndexOfAddr(SegBase(seg), pool, object); + if (!AMS_ALLOCED(seg, i)) { + /* This grain is free */ + object = AddrAdd(object, PoolAlignment(pool)); + continue; + } + object = AddrAdd(object, format->headerSize); + next = format->skip(object); + next = AddrSub(next, format->headerSize); + AVER(AddrIsAligned(next, PoolAlignment(pool))); + if (!amsseg->colourTablesInUse || !AMS_IS_WHITE(seg, i)) + (*f)(object, pool->format, pool, p, s); + object = next; + } } @@ -1663,9 +1691,9 @@ static void AMSFreeWalk(Pool pool, FreeBlockVisitor f, void *p) ams = PoolAMS(pool); AVERT(AMS, ams); - ring = &ams->segRing; + ring = PoolSegRing(AMSPool(ams)); RING_FOR(node, ring, nextNode) { - AMSSegFreeWalk(RING_ELT(AMSSeg, segRing, node), f, p); + AMSSegFreeWalk(Seg2AMSSeg(SegOfPoolRing(node)), f, p); } } @@ -1680,7 +1708,7 @@ static Size AMSTotalSize(Pool pool) ams = PoolAMS(pool); AVERT(AMS, ams); - return ams->pgen.totalSize; + return ams->pgen->totalSize; } @@ -1694,7 +1722,7 @@ static Size AMSFreeSize(Pool pool) ams = PoolAMS(pool); AVERT(AMS, ams); - return ams->pgen.freeSize; + return ams->pgen->freeSize; } @@ -1702,26 +1730,20 @@ static Size AMSFreeSize(Pool pool) * * Iterates over the segments, describing all of them. */ -static Res AMSDescribe(Pool pool, mps_lib_FILE *stream, Count depth) + +static Res AMSDescribe(Inst inst, mps_lib_FILE *stream, Count depth) { - AMS ams; - Ring node, nextNode; + Pool pool = CouldBeA(AbstractPool, inst); + AMS ams = CouldBeA(AMSPool, pool); + Ring ring, node, nextNode; Res res; - if (!TESTT(Pool, pool)) - return ResFAIL; - ams = PoolAMS(pool); - if (!TESTT(AMS, ams)) - return ResFAIL; + if (!TESTC(AMSPool, ams)) + return ResPARAM; if (stream == NULL) - return ResFAIL; + return ResPARAM; - res = WriteF(stream, depth, - "AMS $P {\n", (WriteFP)ams, - " pool $P ($U)\n", - (WriteFP)pool, (WriteFU)pool->serial, - " grain shift $U\n", (WriteFU)ams->grainShift, - NULL); + res = NextMethod(Inst, AMSPool, describe)(inst, stream, depth); if (res != ResOK) return res; @@ -1732,17 +1754,13 @@ static Res AMSDescribe(Pool pool, mps_lib_FILE *stream, Count depth) if (res != ResOK) return res; - RING_FOR(node, &ams->segRing, nextNode) { - AMSSeg amsseg = RING_ELT(AMSSeg, segRing, node); - res = SegDescribe(AMSSeg2Seg(amsseg), stream, depth + 2); + ring = PoolSegRing(AMSPool(ams)); + RING_FOR(node, ring, nextNode) { + res = SegDescribe(SegOfPoolRing(node), stream, depth + 2); if (res != ResOK) return res; } - res = WriteF(stream, depth, "} AMS $P\n",(WriteFP)ams, NULL); - if (res != ResOK) - return res; - return ResOK; } @@ -1752,31 +1770,21 @@ static Res AMSDescribe(Pool pool, mps_lib_FILE *stream, Count depth) /* contains the type definition. Hence the use */ /* of DEFINE_CLASS rather than DEFINE_POOL_CLASS */ -DEFINE_CLASS(AMSPoolClass, this) +DEFINE_CLASS(Pool, AMSPool, klass) { - INHERIT_CLASS(this, AbstractCollectPoolClass); - PoolClassMixInFormat(this); - this->name = "AMS"; - this->size = sizeof(AMSStruct); - this->offset = offsetof(AMSStruct, poolStruct); - this->varargs = AMSVarargs; - this->init = AMSInit; - this->finish = AMSFinish; - this->bufferClass = RankBufClassGet; - this->bufferFill = AMSBufferFill; - this->bufferEmpty = AMSBufferEmpty; - 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); + INHERIT_CLASS(klass, AMSPool, AbstractCollectPool); + klass->instClassStruct.describe = AMSDescribe; + klass->instClassStruct.finish = AMSFinish; + klass->size = sizeof(AMSStruct); + klass->varargs = AMSVarargs; + klass->init = AMSInit; + klass->bufferClass = RankBufClassGet; + klass->bufferFill = AMSBufferFill; + klass->segPoolGen = amsSegPoolGen; + klass->freewalk = AMSFreeWalk; + klass->totalSize = AMSTotalSize; + klass->freeSize = AMSFreeSize; + AVERT(PoolClass, klass); } @@ -1796,15 +1804,14 @@ static PoolDebugMixin AMSDebugMixin(Pool pool) /* AMSDebugPoolClass -- the class definition for the debug version */ -DEFINE_POOL_CLASS(AMSDebugPoolClass, this) +DEFINE_CLASS(Pool, AMSDebugPool, klass) { - INHERIT_CLASS(this, AMSPoolClass); - PoolClassMixInDebug(this); - this->name = "AMSDBG"; - this->size = sizeof(AMSDebugStruct); - this->varargs = AMSDebugVarargs; - this->debugMixin = AMSDebugMixin; - AVERT(PoolClass, this); + INHERIT_CLASS(klass, AMSDebugPool, AMSPool); + PoolClassMixInDebug(klass); + klass->size = sizeof(AMSDebugStruct); + klass->varargs = AMSDebugVarargs; + klass->debugMixin = AMSDebugMixin; + AVERT(PoolClass, klass); } @@ -1812,7 +1819,7 @@ DEFINE_POOL_CLASS(AMSDebugPoolClass, this) mps_pool_class_t mps_class_ams(void) { - return (mps_pool_class_t)AMSPoolClassGet(); + return (mps_pool_class_t)CLASS(AMSPool); } @@ -1820,7 +1827,7 @@ mps_pool_class_t mps_class_ams(void) mps_pool_class_t mps_class_ams_debug(void) { - return (mps_pool_class_t)AMSDebugPoolClassGet(); + return (mps_pool_class_t)CLASS(AMSDebugPool); } @@ -1829,14 +1836,15 @@ mps_pool_class_t mps_class_ams_debug(void) Bool AMSCheck(AMS ams) { CHECKS(AMS, ams); + CHECKC(AMSPool, ams); CHECKD(Pool, AMSPool(ams)); - CHECKL(IsSubclassPoly(AMSPool(ams)->class, AMSPoolClassGet())); - CHECKL(PoolAlignment(AMSPool(ams)) == AMSGrainsSize(ams, (Size)1)); + CHECKL(IsA(AMSPool, ams)); CHECKL(PoolAlignment(AMSPool(ams)) == AMSPool(ams)->format->alignment); - CHECKD(PoolGen, &ams->pgen); + if (ams->pgen != NULL) { + CHECKL(ams->pgen == &ams->pgenStruct); + CHECKD(PoolGen, ams->pgen); + } CHECKL(FUNCHECK(ams->segSize)); - CHECKD_NOSIG(Ring, &ams->segRing); - CHECKL(FUNCHECK(ams->allocRing)); CHECKL(FUNCHECK(ams->segsDestroy)); CHECKL(FUNCHECK(ams->segClass)); @@ -1846,7 +1854,7 @@ Bool AMSCheck(AMS ams) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2016 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/poolams.h b/mps/code/poolams.h index 8617063fc80..31be405ded6 100644 --- a/mps/code/poolams.h +++ b/mps/code/poolams.h @@ -10,6 +10,7 @@ #define poolams_h #include "mpmtypes.h" +#include "mpm.h" #include "mpmst.h" #include "ring.h" #include "bt.h" @@ -40,12 +41,10 @@ typedef Res (*AMSSegSizePolicyFunction)(Size *sizeReturn, typedef struct AMSStruct { PoolStruct poolStruct; /* generic pool structure */ - Shift grainShift; /* log2 of grain size */ - PoolGenStruct pgen; /* generation representing the pool */ + PoolGenStruct pgenStruct; /* generation representing the pool */ + PoolGen pgen; /* NULL or pointer to pgenStruct field */ Size size; /* total segment size of the pool */ AMSSegSizePolicyFunction segSize; /* SegSize policy */ - RingStruct segRing; /* ring of segments in the pool */ - AMSRingFunction allocRing; /* fn to get the ring to allocate from */ AMSSegsDestroyFunction segsDestroy; AMSSegClassFunction segClass;/* fn to get the class for segments */ Bool shareAllocTable; /* the alloc table is also used as white table */ @@ -56,11 +55,11 @@ typedef struct AMSStruct { typedef struct AMSSegStruct { GCSegStruct gcSegStruct; /* superclass fields must come first */ AMS ams; /* owning ams */ - RingStruct segRing; /* ring that this seg belongs to */ Count grains; /* total grains */ Count freeGrains; /* free grains */ - Count oldGrains; /* grains allocated prior to last collection */ + Count bufferedGrains; /* grains in buffers */ Count newGrains; /* grains allocated since last collection */ + Count oldGrains; /* grains allocated prior to last collection */ Bool allocTableInUse; /* allocTable is used */ Index firstFree; /* 1st free grain, if allocTable is not used */ BT allocTable; /* set if grain is allocated */ @@ -83,22 +82,6 @@ typedef struct AMSSegStruct { #define AMSPool(ams) (&(ams)->poolStruct) -/* macros for abstracting index/address computations */ -/* */ - -/* only use when size is a multiple of the grain size */ -#define AMSGrains(ams, size) ((size) >> (ams)->grainShift) - -#define AMSGrainsSize(ams, grains) ((grains) << (ams)->grainShift) - -#define AMSSegShift(seg) (Seg2AMSSeg(seg)->ams->grainShift) - -#define AMS_ADDR_INDEX(seg, addr) \ - ((Index)(AddrOffset(SegBase(seg), addr) >> AMSSegShift(seg))) -#define AMS_INDEX_ADDR(seg, index) \ - AddrAdd(SegBase(seg), (Size)(index) << AMSSegShift(seg)) - - /* colour ops */ #define AMS_IS_WHITE(seg, index) \ @@ -166,31 +149,30 @@ typedef struct AMSSegStruct { /* the rest */ -extern Res AMSInitInternal(AMS ams, Format format, Chain chain, unsigned gen, - Bool shareAllocTable); -extern void AMSFinish(Pool pool); +extern Res AMSInitInternal(AMS ams, Arena arena, PoolClass klass, + Chain chain, unsigned gen, + Bool shareAllocTable, ArgList args); +extern void AMSFinish(Inst inst); extern Bool AMSCheck(AMS ams); -extern Res AMSScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg); - #define AMSChain(ams) ((ams)->chain) extern void AMSSegFreeWalk(AMSSeg amsseg, FreeBlockVisitor f, void *p); extern void AMSSegFreeCheck(AMSSeg amsseg); - -typedef SegClass AMSSegClass; -typedef SegClassStruct AMSSegClassStruct; -extern AMSSegClass AMSSegClassGet(void); extern Bool AMSSegCheck(AMSSeg seg); -typedef PoolClass AMSPoolClass; -typedef PoolClassStruct AMSPoolClassStruct; +/* class declarations */ -extern AMSPoolClass AMSPoolClassGet(void); -extern AMSPoolClass AMSDebugPoolClassGet(void); +typedef AMS AMSPool; +DECLARE_CLASS(Pool, AMSPool, AbstractCollectPool); + +typedef AMS AMSDebugPool; +DECLARE_CLASS(Pool, AMSDebugPool, AMSPool); + +DECLARE_CLASS(Seg, AMSSeg, MutatorSeg); #endif /* poolams_h */ diff --git a/mps/code/poolawl.c b/mps/code/poolawl.c index 6d7c4d6efb2..0003c3a6be8 100644 --- a/mps/code/poolawl.c +++ b/mps/code/poolawl.c @@ -1,7 +1,7 @@ /* poolawl.c: AUTOMATIC WEAK LINKED POOL CLASS * * $Id$ - * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * * * DESIGN @@ -48,6 +48,20 @@ SRCID(poolawl, "$Id$"); #define AWLSig ((Sig)0x519B7A37) /* SIGnature PooL AWL */ +static Bool awlSegBufferFill(Addr *baseReturn, Addr *limitReturn, + Seg seg, Size size, RankSet rankSet); +static void awlSegBufferEmpty(Seg seg, Buffer buffer); +static Res awlSegAccess(Seg seg, Arena arena, Addr addr, + AccessSet mode, MutatorContext context); +static Res awlSegWhiten(Seg seg, Trace trace); +static void awlSegGreyen(Seg seg, Trace trace); +static void awlSegBlacken(Seg seg, TraceSet traceSet); +static Res awlSegScan(Bool *totalReturn, Seg seg, ScanState ss); +static Res awlSegFix(Seg seg, ScanState ss, Ref *refIO); +static void awlSegReclaim(Seg seg, Trace trace); +static void awlSegWalk(Seg seg, Format format, FormattedObjectsVisitor f, + void *p, size_t s); + /* awlStat* -- Statistics gathering about instruction emulation * @@ -81,34 +95,32 @@ typedef Addr (*FindDependentFunction)(Addr object); * See */ -typedef struct AWLStruct { +typedef struct AWLPoolStruct { PoolStruct poolStruct; - Shift alignShift; - PoolGenStruct pgen; /* generation representing the pool */ + PoolGenStruct pgenStruct; /* generation representing the pool */ + PoolGen pgen; /* NULL or pointer to pgenStruct */ Count succAccesses; /* number of successive single accesses */ FindDependentFunction findDependent; /* to find a dependent object */ awlStatTotalStruct stats; - Sig sig; -} AWLStruct, *AWL; - -#define PoolAWL(pool) PARENT(AWLStruct, poolStruct, pool) -#define AWLPool(awl) (&(awl)->poolStruct) -#define AWLGrainsSize(awl, grains) ((grains) << (awl)->alignShift) + Sig sig; /* */ +} AWLPoolStruct, *AWL; 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)) +typedef AWL AWLPool; +#define AWLPoolCheck AWLCheck +DECLARE_CLASS(Pool, AWLPool, AbstractCollectPool); /* AWLSegStruct -- AWL segment subclass * - * Subclass of GCSeg + * Colour is represented as follows: + * Black: +alloc +mark +scanned + * White: +alloc -mark -scanned + * Grey: +alloc +mark -scanned + * Free: -alloc ?mark ?scanned */ #define AWLSegSig ((Sig)0x519A3759) /* SIGnature AWL SeG */ @@ -121,19 +133,15 @@ typedef struct AWLSegStruct { BT alloc; Count grains; Count freeGrains; /* free grains */ - Count oldGrains; /* grains allocated prior to last collection */ + Count bufferedGrains; /* grains in buffers */ Count newGrains; /* grains allocated since last collection */ + Count oldGrains; /* grains allocated prior to last collection */ Count singleAccesses; /* number of accesses processed singly */ awlStatSegStruct stats; - Sig sig; + Sig sig; /* */ } AWLSegStruct, *AWLSeg; -#define Seg2AWLSeg(seg) ((AWLSeg)(seg)) -#define AWLSeg2Seg(awlseg) ((Seg)(awlseg)) - - -extern SegClass AWLSegClassGet(void); - +DECLARE_CLASS(Seg, AWLSeg, MutatorSeg); ATTRIBUTE_UNUSED static Bool AWLSegCheck(AWLSeg awlseg) @@ -144,7 +152,8 @@ static Bool AWLSegCheck(AWLSeg awlseg) CHECKL(awlseg->scanned != NULL); CHECKL(awlseg->alloc != NULL); CHECKL(awlseg->grains > 0); - CHECKL(awlseg->grains == awlseg->freeGrains + awlseg->oldGrains + awlseg->newGrains); + CHECKL(awlseg->grains == awlseg->freeGrains + awlseg->bufferedGrains + + awlseg->newGrains + awlseg->oldGrains); return TRUE; } @@ -172,12 +181,9 @@ static void awlStatTotalInit(AWL awl) ARG_DEFINE_KEY(awl_seg_rank_set, RankSet); #define awlKeySegRankSet (&_mps_key_awl_seg_rank_set) -static Res AWLSegInit(Seg seg, Pool pool, Addr base, Size size, - Bool reservoirPermit, ArgList args) +static Res AWLSegInit(Seg seg, Pool pool, Addr base, Size size, ArgList args) { - SegClass super; AWLSeg awlseg; - AWL awl; Arena arena; RankSet rankSet; Count bits; /* number of grains */ @@ -186,12 +192,6 @@ static Res AWLSegInit(Seg seg, Pool pool, Addr base, Size size, void *v; ArgStruct arg; - AVERT(Seg, seg); - awlseg = Seg2AWLSeg(seg); - AVERT(Pool, pool); - arena = PoolArena(pool); - /* no useful checks for base and size */ - AVERT(Bool, reservoirPermit); ArgRequire(&arg, args, awlKeySegRankSet); rankSet = arg.val.u; AVERT(RankSet, rankSet); @@ -199,102 +199,96 @@ static Res AWLSegInit(Seg seg, Pool pool, Addr base, Size size, /* AWL only accepts two ranks */ AVER(RankSetSingle(RankEXACT) == rankSet || RankSetSingle(RankWEAK) == rankSet); - awl = PoolAWL(pool); - AVERT(AWL, awl); /* Initialize the superclass fields first via next-method call */ - super = SEG_SUPERCLASS(AWLSegClass); - res = super->init(seg, pool, base, size, reservoirPermit, args); + res = NextMethod(Seg, AWLSeg, init)(seg, pool, base, size, args); if (res != ResOK) - return res; + goto failSuperInit; + awlseg = CouldBeA(AWLSeg, seg); - bits = size >> awl->alignShift; + AVERT(Pool, pool); + arena = PoolArena(pool); + /* no useful checks for base and size */ + + bits = PoolSizeGrains(pool, size); tableSize = BTSize(bits); - res = ControlAlloc(&v, arena, tableSize, reservoirPermit); + res = ControlAlloc(&v, arena, 3 * tableSize); if (res != ResOK) - goto failControlAllocMark; + goto failControlAlloc; awlseg->mark = v; - res = ControlAlloc(&v, arena, tableSize, reservoirPermit); - if (res != ResOK) - goto failControlAllocScanned; - awlseg->scanned = v; - res = ControlAlloc(&v, arena, tableSize, reservoirPermit); - if (res != ResOK) - goto failControlAllocAlloc; - awlseg->alloc = v; + awlseg->scanned = PointerAdd(v, tableSize); + awlseg->alloc = PointerAdd(v, 2 * tableSize); awlseg->grains = bits; BTResRange(awlseg->mark, 0, bits); BTResRange(awlseg->scanned, 0, bits); BTResRange(awlseg->alloc, 0, bits); SegSetRankAndSummary(seg, rankSet, RefSetUNIV); awlseg->freeGrains = bits; - awlseg->oldGrains = (Count)0; + awlseg->bufferedGrains = (Count)0; awlseg->newGrains = (Count)0; + awlseg->oldGrains = (Count)0; awlseg->singleAccesses = 0; awlStatSegInit(awlseg); + + SetClassOfPoly(seg, CLASS(AWLSeg)); awlseg->sig = AWLSegSig; - AVERT(AWLSeg, awlseg); + AVERC(AWLSeg, awlseg); + return ResOK; -failControlAllocAlloc: - ControlFree(arena, awlseg->scanned, tableSize); -failControlAllocScanned: - ControlFree(arena, awlseg->mark, tableSize); -failControlAllocMark: - super->finish(seg); +failControlAlloc: + NextMethod(Inst, AWLSeg, finish)(MustBeA(Inst, seg)); +failSuperInit: + AVER(res != ResOK); return res; } /* AWLSegFinish -- Finish method for AWL segments */ -static void AWLSegFinish(Seg seg) +static void AWLSegFinish(Inst inst) { - AWL awl; - AWLSeg awlseg; - SegClass super; - Pool pool; + Seg seg = MustBeA(Seg, inst); + AWLSeg awlseg = MustBeA(AWLSeg, seg); + Pool pool = SegPool(seg); + Arena arena = PoolArena(pool); Size tableSize; - Arena arena; Count segGrains; - AVERT(Seg, seg); - awlseg = Seg2AWLSeg(seg); - AVERT(AWLSeg, awlseg); - pool = SegPool(seg); - AVERT(Pool, pool); - awl = PoolAWL(pool); - AVERT(AWL, awl); - arena = PoolArena(pool); - AVERT(Arena, arena); - /* This is one of the few places where it is easy to check */ /* awlseg->grains, so we do */ - segGrains = SegSize(seg) >> awl->alignShift; + segGrains = PoolSizeGrains(pool, SegSize(seg)); AVER(segGrains == awlseg->grains); tableSize = BTSize(segGrains); - ControlFree(arena, awlseg->alloc, tableSize); - ControlFree(arena, awlseg->scanned, tableSize); - ControlFree(arena, awlseg->mark, tableSize); + ControlFree(arena, awlseg->mark, 3 * tableSize); awlseg->sig = SigInvalid; /* finish the superclass fields last */ - super = SEG_SUPERCLASS(AWLSegClass); - super->finish(seg); + NextMethod(Inst, AWLSeg, finish)(inst); } /* AWLSegClass -- Class definition for AWL segments */ -DEFINE_SEG_CLASS(AWLSegClass, class) +DEFINE_CLASS(Seg, AWLSeg, klass) { - INHERIT_CLASS(class, GCSegClass); - SegClassMixInNoSplitMerge(class); /* no support for this (yet) */ - class->name = "AWLSEG"; - class->size = sizeof(AWLSegStruct); - class->init = AWLSegInit; - class->finish = AWLSegFinish; - AVERT(SegClass, class); + INHERIT_CLASS(klass, AWLSeg, MutatorSeg); + SegClassMixInNoSplitMerge(klass); /* no support for this (yet) */ + klass->instClassStruct.finish = AWLSegFinish; + klass->size = sizeof(AWLSegStruct); + klass->init = AWLSegInit; + klass->bufferFill = awlSegBufferFill; + klass->bufferEmpty = awlSegBufferEmpty; + klass->access = awlSegAccess; + klass->whiten = awlSegWhiten; + klass->greyen = awlSegGreyen; + klass->blacken = awlSegBlacken; + klass->scan = awlSegScan; + klass->fix = awlSegFix; + klass->fixEmergency = awlSegFix; + klass->reclaim = awlSegReclaim; + klass->walk = awlSegWalk; + AVERT(SegClass, klass); } @@ -307,7 +301,7 @@ DEFINE_SEG_CLASS(AWLSegClass, class) * AWLSegSALimit is the number of accesses for a single segment in a GC cycle. * AWLTotalSALimit is the total number of accesses during a GC cycle. * - * These should be set in config.h, but are here in static variables so that + * These should be set in config.h, but are here in global variables so that * it's possible to tweak them in a debugger. */ @@ -324,11 +318,12 @@ Bool AWLHaveTotalSALimit = AWL_HAVE_TOTAL_SA_LIMIT; /* Determine whether to permit scanning a single ref. */ -static Bool AWLCanTrySingleAccess(Arena arena, AWL awl, Seg seg, Addr addr) +static Bool awlSegCanTrySingleAccess(Arena arena, Seg seg, Addr addr) { AWLSeg awlseg; + AWL awl; - AVERT(AWL, awl); + AVERT(Arena, arena); AVERT(Seg, seg); AVER(addr != NULL); @@ -350,8 +345,8 @@ static Bool AWLCanTrySingleAccess(Arena arena, AWL awl, Seg seg, Addr addr) if (TraceRankForAccess(arena, seg) == RankWEAK) return FALSE; - awlseg = Seg2AWLSeg(seg); - AVERT(AWLSeg, awlseg); + awlseg = MustBeA(AWLSeg, seg); + awl = MustBeA(AWLPool, SegPool(seg)); /* If there have been too many single accesses in a row then don't keep trying them, even if it means retaining objects. */ @@ -382,20 +377,19 @@ static Bool AWLCanTrySingleAccess(Arena arena, AWL awl, Seg seg, Addr addr) static void AWLNoteRefAccess(AWL awl, Seg seg, Addr addr) { - AWLSeg awlseg; + AWLSeg awlseg = MustBeA(AWLSeg, seg); AVERT(AWL, awl); - AVERT(Seg, seg); AVER(addr != NULL); - awlseg = Seg2AWLSeg(seg); - AVERT(AWLSeg, awlseg); awlseg->singleAccesses++; /* increment seg count of ref accesses */ - if (addr == awlseg->stats.lastAccess) { - /* If this is a repeated access, increment count */ - STATISTIC(awlseg->stats.sameAccesses++); - } - STATISTIC(awlseg->stats.lastAccess = addr); + STATISTIC({ + if (addr == awlseg->stats.lastAccess) { + /* If this is a repeated access, increment count */ + ++ awlseg->stats.sameAccesses; + } + awlseg->stats.lastAccess = addr; + }); awl->succAccesses++; /* Note a new successive access */ } @@ -414,108 +408,102 @@ static void AWLNoteSegAccess(AWL awl, Seg seg, Addr addr) /* Record a scan of a segment which wasn't provoked by an access */ -static void AWLNoteScan(AWL awl, Seg seg, ScanState ss) +static void AWLNoteScan(Seg seg, ScanState ss) { - AWLSeg awlseg; - - AVERT(AWL, awl); - AVERT(Seg, seg); - awlseg = Seg2AWLSeg(seg); - AVERT(AWLSeg, awlseg); + AWLSeg awlseg = MustBeA(AWLSeg, seg); + UNUSED(ss); /* .assume.mixedrank */ /* .assume.samerank */ - /* If this segment has any RankWEAK references, then */ - /* record statistics about whether weak splatting is being lost. */ if (RankSetIsMember(SegRankSet(seg), RankWEAK)) { - if (RankWEAK == ss->rank) { - /* This is "successful" scan at proper rank. */ - STATISTIC(awl->stats.goodScans++); - if (0 < awlseg->singleAccesses) { - /* Accesses have been proceesed singly */ - /* Record that we genuinely did save a protection-provoked scan */ - STATISTIC(awl->stats.savedScans++); - STATISTIC(awl->stats.savedAccesses += awlseg->singleAccesses); + STATISTIC({ + /* If this segment has any RankWEAK references, then record + * statistics about whether weak splatting is being lost. */ + AWL awl = MustBeA(AWLPool, SegPool(seg)); + if (RankWEAK == ss->rank) { + /* This is "successful" scan at proper rank. */ + ++ awl->stats.goodScans; + if (0 < awlseg->singleAccesses) { + /* Accesses have been proceesed singly. Record that we + * genuinely did save a protection-provoked scan */ + ++ awl->stats.savedScans; + awl->stats.savedAccesses += awlseg->singleAccesses; + } + } else { + /* This is "failed" scan at improper rank. */ + ++ awl->stats.badScans; } - } else { - /* This is "failed" scan at improper rank. */ - STATISTIC(awl->stats.badScans++); - } + awlStatSegInit(awlseg); + }); /* Reinitialize the segment statistics */ awlseg->singleAccesses = 0; - STATISTIC(awlStatSegInit(awlseg)); } } -/* AWLSegCreate -- Create a new segment of at least given size */ +/* awlSegBufferFill -- try filling buffer from segment */ -static Res AWLSegCreate(AWLSeg *awlsegReturn, - RankSet rankSet, Pool pool, Size size, - Bool reservoirPermit) +static Bool awlSegBufferFill(Addr *baseReturn, Addr *limitReturn, + Seg seg, Size size, RankSet rankSet) { - AWL awl; - Seg seg; - AWLSeg awlseg; - Res res; - Arena arena; - - AVER(awlsegReturn != NULL); - AVERT(RankSet, rankSet); - AVERT(Pool, pool); - AVER(size > 0); - AVERT(Bool, reservoirPermit); - - awl = PoolAWL(pool); - AVERT(AWL, awl); - - arena = PoolArena(pool); - AVERT(Arena, 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 = PoolGenAlloc(&seg, &awl->pgen, AWLSegClassGet(), size, - reservoirPermit, args); - } MPS_ARGS_END(args); - if (res != ResOK) - return res; - - awlseg = Seg2AWLSeg(seg); - AVERT(AWLSeg, awlseg); - - *awlsegReturn = awlseg; - return ResOK; -} - - -/* AWLSegAlloc -- allocate an object in a given segment */ - -static Bool AWLSegAlloc(Addr *baseReturn, Addr *limitReturn, - AWLSeg awlseg, AWL awl, Size size) -{ - Count n; /* number of grains equivalent to alloc size */ - Index i, j; - Seg seg; + Index baseIndex, limitIndex; + AWLSeg awlseg = MustBeA(AWLSeg, seg); + Pool pool = SegPool(seg); + Count requestedGrains, segGrains, allocatedGrains; + Addr segBase, base, limit; AVER(baseReturn != NULL); AVER(limitReturn != NULL); - AVERT(AWLSeg, awlseg); - AVERT(AWL, awl); + AVER(SizeIsAligned(size, PoolAlignment(pool))); AVER(size > 0); - AVER(AWLGrainsSize(awl, size) >= size); - seg = AWLSeg2Seg(awlseg); + AVERT(RankSet, rankSet); - if (size > SegSize(seg)) + requestedGrains = PoolSizeGrains(pool, size); + if (awlseg->freeGrains < requestedGrains) + /* Not enough space to satisfy the request. */ return FALSE; - n = size >> awl->alignShift; - if (!BTFindLongResRange(&i, &j, awlseg->alloc, 0, awlseg->grains, n)) + + if (SegHasBuffer(seg)) + /* Don't bother trying to allocate from a buffered segment */ return FALSE; - *baseReturn = awlAddrOfIndex(SegBase(seg), awl, i); - *limitReturn = awlAddrOfIndex(SegBase(seg),awl, j); + + if (rankSet != SegRankSet(seg)) + /* Can't satisfy required rank set. */ + return FALSE; + + segGrains = PoolSizeGrains(pool, SegSize(seg)); + if (awlseg->freeGrains == segGrains) { + /* Whole segment is free: no need for a search. */ + baseIndex = 0; + limitIndex = segGrains; + goto found; + } + + if (!BTFindLongResRange(&baseIndex, &limitIndex, awlseg->alloc, + 0, segGrains, requestedGrains)) + return FALSE; + +found: + AVER(baseIndex < limitIndex); + allocatedGrains = limitIndex - baseIndex; + AVER(requestedGrains <= allocatedGrains); + AVER(BTIsResRange(awlseg->alloc, baseIndex, limitIndex)); + BTSetRange(awlseg->alloc, baseIndex, limitIndex); + /* Objects are allocated black. */ + /* TODO: This should depend on trace phase. */ + BTSetRange(awlseg->mark, baseIndex, limitIndex); + BTSetRange(awlseg->scanned, baseIndex, limitIndex); + AVER(awlseg->freeGrains >= allocatedGrains); + awlseg->freeGrains -= allocatedGrains; + awlseg->bufferedGrains += allocatedGrains; + + segBase = SegBase(seg); + base = PoolAddrOfIndex(segBase, pool, baseIndex); + limit = PoolAddrOfIndex(segBase, pool, limitIndex); + PoolGenAccountForFill(PoolSegPoolGen(pool, seg), AddrOffset(base, limit)); + + *baseReturn = base; + *limitReturn = limit; return TRUE; } @@ -546,38 +534,40 @@ static Addr awlNoDependent(Addr addr) ARG_DEFINE_KEY(AWL_FIND_DEPENDENT, Fun); -static Res AWLInit(Pool pool, ArgList args) +static Res AWLInit(Pool pool, Arena arena, PoolClass klass, ArgList args) { AWL awl; - Format format; FindDependentFunction findDependent = awlNoDependent; Chain chain; Res res; ArgStruct arg; unsigned gen = AWL_GEN_DEFAULT; - /* Weak check, as half-way through initialization. */ AVER(pool != NULL); + AVERT(Arena, arena); + AVERT(ArgList, args); + UNUSED(klass); /* used for debug pools only */ - awl = PoolAWL(pool); - - ArgRequire(&arg, args, MPS_KEY_FORMAT); - format = arg.val.format; 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 { - chain = ArenaGlobals(PoolArena(pool))->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, format); - AVER(FormatArena(format) == PoolArena(pool)); - pool->format = format; - pool->alignment = format->alignment; + res = NextMethod(Pool, AWLPool, init)(pool, arena, klass, args); + if (res != ResOK) + goto failNextInit; + awl = CouldBeA(AWLPool, pool); + + /* Ensure a format was supplied in the argument list. */ + AVER(pool->format != NULL); + pool->alignment = pool->format->alignment; + pool->alignShift = SizeLog2(pool->alignment); AVER(FUNCHECK(findDependent)); awl->findDependent = findDependent; @@ -586,20 +576,27 @@ static Res AWLInit(Pool pool, ArgList args) AVER(gen <= ChainGens(chain)); AVER(chain->arena == PoolArena(pool)); - res = PoolGenInit(&awl->pgen, ChainGen(chain, gen), pool); - if (res != ResOK) - goto failGenInit; + awl->pgen = NULL; - awl->alignShift = SizeLog2(PoolAlignment(pool)); awl->succAccesses = 0; awlStatTotalInit(awl); - awl->sig = AWLSig; - AVERT(AWL, awl); - EVENT2(PoolInitAWL, pool, format); + SetClassOfPoly(pool, CLASS(AWLPool)); + awl->sig = AWLSig; + AVERC(AWLPool, awl); + + res = PoolGenInit(&awl->pgenStruct, ChainGen(chain, gen), pool); + if (res != ResOK) + goto failGenInit; + awl->pgen = &awl->pgenStruct; + + EVENT2(PoolInitAWL, pool, pool->format); + return ResOK; failGenInit: + NextMethod(Inst, AWLPool, finish)(MustBeA(Inst, pool)); +failNextInit: AVER(res != ResOK); return res; } @@ -607,146 +604,129 @@ static Res AWLInit(Pool pool, ArgList args) /* AWLFinish -- finish an AWL pool */ -static void AWLFinish(Pool pool) +static void AWLFinish(Inst inst) { - AWL awl; + Pool pool = MustBeA(AbstractPool, inst); + AWL awl = MustBeA(AWLPool, pool); Ring ring, node, nextNode; - AVERT(Pool, pool); - - awl = PoolAWL(pool); - AVERT(AWL, awl); - ring = &pool->segRing; RING_FOR(node, ring, nextNode) { Seg seg = SegOfPoolRing(node); - AWLSeg awlseg = Seg2AWLSeg(seg); + AWLSeg awlseg = MustBeA(AWLSeg, seg); + AVER(!SegHasBuffer(seg)); AVERT(AWLSeg, awlseg); - PoolGenFree(&awl->pgen, seg, - AWLGrainsSize(awl, awlseg->freeGrains), - AWLGrainsSize(awl, awlseg->oldGrains), - AWLGrainsSize(awl, awlseg->newGrains), + AVER(awlseg->bufferedGrains == 0); + PoolGenFree(awl->pgen, seg, + PoolGrainsSize(pool, awlseg->freeGrains), + PoolGrainsSize(pool, awlseg->oldGrains), + PoolGrainsSize(pool, awlseg->newGrains), FALSE); } awl->sig = SigInvalid; - PoolGenFinish(&awl->pgen); + PoolGenFinish(awl->pgen); + + NextMethod(Inst, AWLPool, finish)(inst); } -/* AWLBufferFill -- BufferFill method for AWL */ +/* awlBufferFill -- BufferFill method for AWL */ -static Res AWLBufferFill(Addr *baseReturn, Addr *limitReturn, - Pool pool, Buffer buffer, Size size, - Bool reservoirPermit) +static Res awlBufferFill(Addr *baseReturn, Addr *limitReturn, + Pool pool, Buffer buffer, Size size) { - Addr base, limit; - AWLSeg awlseg; - AWL awl; + AWL awl = MustBeA(AWLPool, pool); Res res; Ring node, nextNode; + RankSet rankSet; + Seg seg; + Bool b; AVER(baseReturn != NULL); AVER(limitReturn != NULL); - AVERT(Pool, pool); - AVERT(Buffer, buffer); + AVERC(Buffer, buffer); + AVER(BufferIsReset(buffer)); AVER(size > 0); - AVERT(Bool, reservoirPermit); - - awl = PoolAWL(pool); - AVERT(AWL, awl); + AVER(SizeIsAligned(size, PoolAlignment(pool))); + rankSet = BufferRankSet(buffer); RING_FOR(node, &pool->segRing, nextNode) { - Seg seg; - seg = SegOfPoolRing(node); - AVERT(Seg, seg); - awlseg = Seg2AWLSeg(seg); - AVERT(AWLSeg, awlseg); - - /* 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) - && AWLGrainsSize(awl, awlseg->freeGrains) >= size - && AWLSegAlloc(&base, &limit, awlseg, awl, size)) - goto found; + if (SegBufferFill(baseReturn, limitReturn, seg, size, rankSet)) + return ResOK; } - /* No free space in existing awlsegs, so create new awlseg */ - - res = AWLSegCreate(&awlseg, BufferRankSet(buffer), pool, size, - reservoirPermit); + /* No segment had enough space, so make a new one. */ + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD_FIELD(args, awlKeySegRankSet, u, BufferRankSet(buffer)); + res = PoolGenAlloc(&seg, awl->pgen, CLASS(AWLSeg), + SizeArenaGrains(size, PoolArena(pool)), args); + } MPS_ARGS_END(args); if (res != ResOK) return res; - base = SegBase(AWLSeg2Seg(awlseg)); - limit = SegLimit(AWLSeg2Seg(awlseg)); - -found: - { - Index i, j; - Seg seg = AWLSeg2Seg(awlseg); - i = awlIndexOfAddr(SegBase(seg), awl, base); - j = awlIndexOfAddr(SegBase(seg), awl, limit); - AVER(i < j); - BTSetRange(awlseg->alloc, i, j); - /* Objects are allocated black. */ - /* Shouldn't this depend on trace phase? @@@@ */ - BTSetRange(awlseg->mark, i, j); - BTSetRange(awlseg->scanned, i, j); - AVER(awlseg->freeGrains >= j - i); - awlseg->freeGrains -= j - i; - awlseg->newGrains += j - i; - PoolGenAccountForFill(&awl->pgen, AddrOffset(base, limit), FALSE); - } - *baseReturn = base; - *limitReturn = limit; + b = SegBufferFill(baseReturn, limitReturn, seg, size, rankSet); + AVER(b); return ResOK; } -/* AWLBufferEmpty -- BufferEmpty method for AWL */ +/* awlSegBufferEmpty -- empty buffer to segment */ -static void AWLBufferEmpty(Pool pool, Buffer buffer, Addr init, Addr limit) +static void awlSegBufferEmpty(Seg seg, Buffer buffer) { - AWL awl; - AWLSeg awlseg; - Seg seg; - Addr segBase; - Index i, j; + AWLSeg awlseg = MustBeA(AWLSeg, seg); + Pool pool = SegPool(seg); + Addr segBase, bufferBase, init, limit; + Index initIndex, limitIndex; + Count unusedGrains, usedGrains; - AVERT(Pool, pool); - AVERT(Buffer, buffer); - seg = BufferSeg(buffer); AVERT(Seg, seg); - AVER(init <= limit); - - awl = PoolAWL(pool); - AVERT(AWL, awl); - awlseg = Seg2AWLSeg(seg); - AVERT(AWLSeg, awlseg); - + AVERT(Buffer, buffer); segBase = SegBase(seg); + bufferBase = BufferBase(buffer); + init = BufferGetInit(buffer); + limit = BufferLimit(buffer); + AVER(segBase <= bufferBase); + AVER(bufferBase <= init); + AVER(init <= limit); + AVER(limit <= SegLimit(seg)); - i = awlIndexOfAddr(segBase, awl, init); - j = awlIndexOfAddr(segBase, awl, limit); - AVER(i <= j); - if (i < j) { - BTResRange(awlseg->alloc, i, j); - AVER(awlseg->newGrains >= j - i); - awlseg->newGrains -= j - i; - awlseg->freeGrains += j - i; - PoolGenAccountForEmpty(&awl->pgen, AddrOffset(init, limit), FALSE); - } + initIndex = PoolIndexOfAddr(segBase, pool, init); + limitIndex = PoolIndexOfAddr(segBase, pool, limit); + + if (initIndex < limitIndex) + BTResRange(awlseg->alloc, initIndex, limitIndex); + + unusedGrains = limitIndex - initIndex; + AVER(unusedGrains <= awlseg->bufferedGrains); + usedGrains = awlseg->bufferedGrains - unusedGrains; + awlseg->freeGrains += unusedGrains; + awlseg->bufferedGrains = 0; + awlseg->newGrains += usedGrains; + + PoolGenAccountForEmpty(PoolSegPoolGen(pool, seg), + PoolGrainsSize(pool, usedGrains), + PoolGrainsSize(pool, unusedGrains), FALSE); } -/* AWLWhiten -- segment condemning method */ +/* awlSegPoolGen -- get pool generation for an AWL segment */ -/* awlRangeWhiten -- helper function that works on a range. +static PoolGen awlSegPoolGen(Pool pool, Seg seg) +{ + AWL awl = MustBeA(AWLPool, pool); + AVERT(Seg, seg); + return awl->pgen; +} + + +/* awlSegWhiten -- segment condemning method */ + +/* awlSegRangeWhiten -- helper function that works on a range. * - * This function abstracts common code from AWLWhiten. + * This function abstracts common code from awlSegWhiten. */ -static void awlRangeWhiten(AWLSeg awlseg, Index base, Index limit) +static void awlSegRangeWhiten(AWLSeg awlseg, Index base, Index limit) { if(base != limit) { AVER(base < limit); @@ -756,36 +736,31 @@ static void awlRangeWhiten(AWLSeg awlseg, Index base, Index limit) } } -static Res AWLWhiten(Pool pool, Trace trace, Seg seg) +static Res awlSegWhiten(Seg seg, Trace trace) { - AWL awl; - AWLSeg awlseg; + AWLSeg awlseg = MustBeA(AWLSeg, seg); + Pool pool = SegPool(seg); + PoolGen pgen = PoolSegPoolGen(pool, seg); Buffer buffer; - Count uncondemned; + Count agedGrains, uncondemnedGrains; - /* All parameters checked by generic PoolWhiten. */ - - awl = PoolAWL(pool); - AVERT(AWL, awl); - awlseg = Seg2AWLSeg(seg); - AVERT(AWLSeg, awlseg); - buffer = SegBuffer(seg); + /* All parameters checked by generic SegWhiten. */ /* Can only whiten for a single trace, */ /* see */ AVER(SegWhite(seg) == TraceSetEMPTY); - if(buffer == NULL) { - awlRangeWhiten(awlseg, 0, awlseg->grains); - uncondemned = (Count)0; + if (!SegBuffer(&buffer, seg)) { + awlSegRangeWhiten(awlseg, 0, awlseg->grains); + uncondemnedGrains = (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)); - uncondemned = limitIndex - scanLimitIndex; - awlRangeWhiten(awlseg, 0, scanLimitIndex); - awlRangeWhiten(awlseg, limitIndex, awlseg->grains); + Index scanLimitIndex = PoolIndexOfAddr(base, pool, BufferScanLimit(buffer)); + Index limitIndex = PoolIndexOfAddr(base, pool, BufferLimit(buffer)); + uncondemnedGrains = limitIndex - scanLimitIndex; + awlSegRangeWhiten(awlseg, 0, scanLimitIndex); + awlSegRangeWhiten(awlseg, limitIndex, awlseg->grains); /* Check the buffer is black. */ /* This really ought to change when we have a non-trivial */ @@ -796,19 +771,29 @@ static Res AWLWhiten(Pool pool, Trace trace, Seg seg) } } - 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)); + /* The unused part of the buffer remains buffered: the rest becomes old. */ + AVER(awlseg->bufferedGrains >= uncondemnedGrains); + agedGrains = awlseg->bufferedGrains - uncondemnedGrains; + PoolGenAccountForAge(pgen, PoolGrainsSize(pool, agedGrains), + PoolGrainsSize(pool, awlseg->newGrains), FALSE); + awlseg->oldGrains += agedGrains + awlseg->newGrains; + awlseg->bufferedGrains = uncondemnedGrains; + awlseg->newGrains = 0; + + if (awlseg->oldGrains > 0) { + GenDescCondemned(pgen->gen, trace, + PoolGrainsSize(pool, awlseg->oldGrains)); + SegSetWhite(seg, TraceSetAdd(SegWhite(seg), trace)); + } + return ResOK; } -/* AWLGrey -- Grey method for AWL pools */ +/* awlSegGreyen -- Greyen method for AWL segments */ -/* AWLRangeGrey -- subroutine for AWLGrey */ -static void AWLRangeGrey(AWLSeg awlseg, Index base, Index limit) +/* awlSegRangeGreyen -- subroutine for awlSegGreyen */ +static void awlSegRangeGreyen(AWLSeg awlseg, Index base, Index limit) { /* AWLSeg not checked as that's already been done */ AVER(limit <= awlseg->grains); @@ -821,54 +806,43 @@ static void AWLRangeGrey(AWLSeg awlseg, Index base, Index limit) } } -static void AWLGrey(Pool pool, Trace trace, Seg seg) +static void awlSegGreyen(Seg seg, Trace trace) { - AVERT(Pool, pool); - AVERT(Trace, trace); + Buffer buffer; + Pool pool; + AVERT(Seg, seg); + AVERT(Trace, trace); + pool = SegPool(seg); + AVER(PoolArena(pool) == trace->arena); if (!TraceSetIsMember(SegWhite(seg), trace)) { - AWL awl; - AWLSeg awlseg; - - awl = PoolAWL(pool); - AVERT(AWL, awl); - awlseg = Seg2AWLSeg(seg); - AVERT(AWLSeg, awlseg); + AWLSeg awlseg = MustBeA(AWLSeg, seg); SegSetGrey(seg, TraceSetAdd(SegGrey(seg), trace)); - if (SegBuffer(seg) != NULL) { + if (SegBuffer(&buffer, seg)) { Addr base = SegBase(seg); - Buffer buffer = SegBuffer(seg); - AWLRangeGrey(awlseg, - 0, - awlIndexOfAddr(base, awl, BufferScanLimit(buffer))); - AWLRangeGrey(awlseg, - awlIndexOfAddr(base, awl, BufferLimit(buffer)), - awlseg->grains); + awlSegRangeGreyen(awlseg, + 0, + PoolIndexOfAddr(base, pool, BufferScanLimit(buffer))); + awlSegRangeGreyen(awlseg, + PoolIndexOfAddr(base, pool, BufferLimit(buffer)), + awlseg->grains); } else { - AWLRangeGrey(awlseg, 0, awlseg->grains); + awlSegRangeGreyen(awlseg, 0, awlseg->grains); } } } -/* AWLBlacken -- Blacken method for AWL pools */ +/* awlSegBlacken -- Blacken method for AWL segments */ -static void AWLBlacken(Pool pool, TraceSet traceSet, Seg seg) +static void awlSegBlacken(Seg seg, TraceSet traceSet) { - AWL awl; - AWLSeg awlseg; + AWLSeg awlseg = MustBeA(AWLSeg, seg); - AVERT(Pool, pool); AVERT(TraceSet, traceSet); - AVERT(Seg, seg); - - awl = PoolAWL(pool); - AVERT(AWL, awl); - awlseg = Seg2AWLSeg(seg); - AVERT(AWLSeg, awlseg); BTSetRange(awlseg->scanned, 0, awlseg->grains); } @@ -910,42 +884,29 @@ static Res awlScanObject(Arena arena, AWL awl, ScanState ss, } -/* awlScanSinglePass -- a single scan pass over a segment */ +/* awlSegScanSinglePass -- a single scan pass over a segment */ -static Res awlScanSinglePass(Bool *anyScannedReturn, - ScanState ss, Pool pool, - Seg seg, Bool scanAllObjects) +static Res awlSegScanSinglePass(Bool *anyScannedReturn, ScanState ss, + Seg seg, Bool scanAllObjects) { - Addr base, limit, bufferScanLimit; + AWLSeg awlseg = MustBeA(AWLSeg, seg); + Pool pool = SegPool(seg); + AWL awl = MustBeA(AWLPool, pool); + Arena arena = PoolArena(pool); + Buffer buffer; + Format format = pool->format; + Addr base = SegBase(seg); + Addr limit = SegLimit(seg); + Addr bufferScanLimit; Addr p; Addr hp; - Arena arena; - AWL awl; - AWLSeg awlseg; - Buffer buffer; - Format format; AVERT(ScanState, ss); - AVERT(Pool, pool); - AVERT(Seg, seg); AVERT(Bool, scanAllObjects); - awl = PoolAWL(pool); - AVERT(AWL, awl); - arena = PoolArena(pool); - AVERT(Arena, arena); - - format = pool->format; - AVERT(Format, format); - - awlseg = Seg2AWLSeg(seg); - AVERT(AWLSeg, awlseg); *anyScannedReturn = FALSE; - base = SegBase(seg); - limit = SegLimit(seg); p = base; - buffer = SegBuffer(seg); - if (buffer != NULL && BufferScanLimit(buffer) != BufferLimit(buffer)) + if (SegBuffer(&buffer, seg) && BufferScanLimit(buffer) != BufferLimit(buffer)) bufferScanLimit = BufferScanLimit(buffer); else bufferScanLimit = limit; @@ -960,7 +921,7 @@ static Res awlScanSinglePass(Bool *anyScannedReturn, continue; } - i = awlIndexOfAddr(base, awl, p); + i = PoolIndexOfAddr(base, pool, p); if (!BTGet(awlseg->alloc, i)) { p = AddrAdd(p, PoolAlignment(pool)); continue; @@ -988,27 +949,18 @@ static Res awlScanSinglePass(Bool *anyScannedReturn, } -/* AWLScan -- segment scan method for AWL */ +/* awlSegScan -- segment scan method for AWL */ -static Res AWLScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg) +static Res awlSegScan(Bool *totalReturn, Seg seg, ScanState ss) { - AWL awl; - AWLSeg awlseg; Bool anyScanned; Bool scanAllObjects; Res res; AVER(totalReturn != NULL); AVERT(ScanState, ss); - AVERT(Pool, pool); AVERT(Seg, seg); - awlseg = Seg2AWLSeg(seg); - AVERT(AWLSeg, awlseg); - - awl = PoolAWL(pool); - AVERT(AWL, awl); - /* If the scanner isn't going to scan all the objects then the */ /* summary of the unscanned objects must be added into the scan */ /* state summary, so that it's a valid summary of the entire */ @@ -1024,7 +976,7 @@ static Res AWLScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg) (TraceSetDiff(ss->traces, SegWhite(seg)) != TraceSetEMPTY); do { - res = awlScanSinglePass(&anyScanned, ss, pool, seg, scanAllObjects); + res = awlSegScanSinglePass(&anyScanned, ss, seg, scanAllObjects); if (res != ResOK) { *totalReturn = FALSE; return res; @@ -1035,100 +987,82 @@ static Res AWLScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg) } while(!scanAllObjects && anyScanned); *totalReturn = scanAllObjects; - AWLNoteScan(awl, seg, ss); + AWLNoteScan(seg, ss); return ResOK; } -/* AWLFix -- Fix method for AWL */ +/* awlSegFix -- Fix method for AWL segments */ -static Res AWLFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) +static Res awlSegFix(Seg seg, ScanState ss, Ref *refIO) { + AWLSeg awlseg = MustBeA_CRITICAL(AWLSeg, seg); + Pool pool = SegPool(seg); Ref clientRef; Addr base; Index i; - AWL awl; - AWLSeg awlseg; - AVERT(Pool, pool); - AVERT(ScanState, ss); - AVERT(Seg, seg); - AVER(TraceSetInter(SegWhite(seg), ss->traces) != TraceSetEMPTY); - AVER(refIO != NULL); - - awl = PoolAWL(pool); - AVERT(AWL, awl); - awlseg = Seg2AWLSeg(seg); - AVERT(AWLSeg, awlseg); + AVERT_CRITICAL(ScanState, ss); + AVER_CRITICAL(TraceSetInter(SegWhite(seg), ss->traces) != TraceSetEMPTY); + AVER_CRITICAL(refIO != NULL); clientRef = *refIO; - ss->wasMarked = TRUE; base = AddrSub((Addr)clientRef, pool->format->headerSize); - /* can get an ambiguous reference to 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. */ + + /* Not a real reference if out of bounds. This can happen if an + ambiguous reference is closer to the base of the segment than the + header size. */ if (base < SegBase(seg)) { + AVER(ss->rank == RankAMBIG); return ResOK; } - i = awlIndexOfAddr(SegBase(seg), awl, base); - switch(ss->rank) { - case RankAMBIG: - /* not a real pointer if not aligned or not allocated */ - if (!AddrIsAligned(base, sizeof(void *)) || !BTGet(awlseg->alloc, i)) - return ResOK; - /* falls through */ - case RankEXACT: - case RankFINAL: - case RankWEAK: - if (!BTGet(awlseg->mark, i)) { - ss->wasMarked = FALSE; - if (ss->rank == RankWEAK) { - *refIO = (Ref)0; - } else { - BTSet(awlseg->mark, i); - SegSetGrey(seg, TraceSetUnion(SegGrey(seg), ss->traces)); - } + /* Not a real reference if unaligned. */ + if (!AddrIsAligned(base, PoolAlignment(pool))) { + AVER(ss->rank == RankAMBIG); + return ResOK; + } + + i = PoolIndexOfAddr(SegBase(seg), pool, base); + + /* Not a real reference if unallocated. */ + if (!BTGet(awlseg->alloc, i)) { + AVER(ss->rank == RankAMBIG); + return ResOK; + } + + if (!BTGet(awlseg->mark, i)) { + ss->wasMarked = FALSE; /* */ + if (ss->rank == RankWEAK) { + *refIO = (Ref)0; + } else { + BTSet(awlseg->mark, i); + SegSetGrey(seg, TraceSetUnion(SegGrey(seg), ss->traces)); } - break; - default: - NOTREACHED; - return ResUNIMPL; } return ResOK; } -/* AWLReclaim -- reclaim dead objects in an AWL segment */ +/* awlSegReclaim -- reclaim dead objects in an AWL segment */ -static void AWLReclaim(Pool pool, Trace trace, Seg seg) +static void awlSegReclaim(Seg seg, Trace trace) { - Addr base; - AWL awl; - AWLSeg awlseg; + AWLSeg awlseg = MustBeA(AWLSeg, seg); + Pool pool = SegPool(seg); + PoolGen pgen = PoolSegPoolGen(pool, seg); + Addr base = SegBase(seg); Buffer buffer; - Index i; - Format format; + Bool hasBuffer = SegBuffer(&buffer, seg); + Format format = pool->format; Count reclaimedGrains = (Count)0; Count preservedInPlaceCount = (Count)0; Size preservedInPlaceSize = (Size)0; + Index i; - AVERT(Pool, pool); AVERT(Trace, trace); - AVERT(Seg, seg); - - awl = PoolAWL(pool); - AVERT(AWL, awl); - awlseg = Seg2AWLSeg(seg); - AVERT(AWLSeg, awlseg); - - format = pool->format; - - base = SegBase(seg); - buffer = SegBuffer(seg); i = 0; while(i < awlseg->grains) { @@ -1139,18 +1073,18 @@ static void AWLReclaim(Pool pool, Trace trace, Seg seg) ++i; continue; } - p = awlAddrOfIndex(base, awl, i); - if (buffer != NULL + p = PoolAddrOfIndex(base, pool, i); + if (hasBuffer && p == BufferScanLimit(buffer) && BufferScanLimit(buffer) != BufferLimit(buffer)) { - i = awlIndexOfAddr(base, awl, BufferLimit(buffer)); + i = PoolIndexOfAddr(base, pool, BufferLimit(buffer)); continue; } q = format->skip(AddrAdd(p, format->headerSize)); q = AddrSub(q, format->headerSize); AVER(AddrIsAligned(q, PoolAlignment(pool))); - j = awlIndexOfAddr(base, awl, q); + j = PoolIndexOfAddr(base, pool, q); AVER(j <= awlseg->grains); if(BTGet(awlseg->mark, i)) { AVER(BTGet(awlseg->scanned, i)); @@ -1172,43 +1106,44 @@ static void AWLReclaim(Pool pool, Trace trace, Seg seg) AVER(awlseg->oldGrains >= reclaimedGrains); awlseg->oldGrains -= reclaimedGrains; awlseg->freeGrains += reclaimedGrains; - PoolGenAccountForReclaim(&awl->pgen, AWLGrainsSize(awl, reclaimedGrains), FALSE); + PoolGenAccountForReclaim(pgen, PoolGrainsSize(pool, reclaimedGrains), FALSE); - trace->reclaimSize += AWLGrainsSize(awl, reclaimedGrains); - trace->preservedInPlaceCount += preservedInPlaceCount; - trace->preservedInPlaceSize += preservedInPlaceSize; + STATISTIC(trace->reclaimSize += PoolGrainsSize(pool, reclaimedGrains)); + STATISTIC(trace->preservedInPlaceCount += preservedInPlaceCount); + GenDescSurvived(pgen->gen, trace, 0, preservedInPlaceSize); SegSetWhite(seg, TraceSetDel(SegWhite(seg), trace)); - if (awlseg->freeGrains == awlseg->grains && buffer == NULL) + if (awlseg->freeGrains == awlseg->grains && !hasBuffer) { /* No survivors */ - PoolGenFree(&awl->pgen, seg, - AWLGrainsSize(awl, awlseg->freeGrains), - AWLGrainsSize(awl, awlseg->oldGrains), - AWLGrainsSize(awl, awlseg->newGrains), + AVER(awlseg->bufferedGrains == 0); + PoolGenFree(pgen, seg, + PoolGrainsSize(pool, awlseg->freeGrains), + PoolGrainsSize(pool, awlseg->oldGrains), + PoolGrainsSize(pool, awlseg->newGrains), FALSE); + } } -/* AWLAccess -- handle a barrier hit */ +/* awlSegAccess -- handle a barrier hit */ -static Res AWLAccess(Pool pool, Seg seg, Addr addr, - AccessSet mode, MutatorFaultContext context) +static Res awlSegAccess(Seg seg, Arena arena, Addr addr, + AccessSet mode, MutatorContext context) { AWL awl; Res res; - AVERT(Pool, 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); - + AVERT(MutatorContext, context); + + awl = MustBeA(AWLPool, SegPool(seg)); + /* Attempt scanning a single reference if permitted */ - if(AWLCanTrySingleAccess(PoolArena(pool), awl, seg, addr)) { - res = PoolSingleAccess(pool, seg, addr, mode, context); + if(awlSegCanTrySingleAccess(arena, seg, addr)) { + res = SegSingleAccess(seg, arena, addr, mode, context); switch(res) { case ResOK: AWLNoteRefAccess(awl, seg, addr); @@ -1222,7 +1157,7 @@ static Res AWLAccess(Pool pool, Seg seg, Addr addr, } /* Have to scan the entire seg anyway. */ - res = PoolSegAccess(pool, seg, addr, mode, context); + res = SegWholeAccess(seg, arena, addr, mode, context); if(ResOK == res) { AWLNoteSegAccess(awl, seg, addr); } @@ -1231,28 +1166,19 @@ static Res AWLAccess(Pool pool, Seg seg, Addr addr, } -/* AWLWalk -- walk all objects */ +/* awlSegWalk -- walk all objects */ -static void AWLWalk(Pool pool, Seg seg, FormattedObjectsVisitor f, - void *p, size_t s) +static void awlSegWalk(Seg seg, Format format, FormattedObjectsVisitor f, + void *p, size_t s) { - AWL awl; - AWLSeg awlseg; + AWLSeg awlseg = MustBeA(AWLSeg, seg); + Pool pool = SegPool(seg); Addr object, base, limit; - Format format; - AVERT(Pool, pool); - AVERT(Seg, seg); + AVERT(Format, format); AVER(FUNCHECK(f)); /* p and s are arbitrary closures and can't be checked */ - awl = PoolAWL(pool); - AVERT(AWL, awl); - awlseg = Seg2AWLSeg(seg); - AVERT(AWLSeg, awlseg); - - format = pool->format; - base = SegBase(seg); object = base; limit = SegLimit(seg); @@ -1262,9 +1188,9 @@ static void AWLWalk(Pool pool, Seg seg, FormattedObjectsVisitor f, /* free grain */ Addr next; Index i; + Buffer buffer; - if (SegBuffer(seg) != NULL) { - Buffer buffer = SegBuffer(seg); + if (SegBuffer(&buffer, seg)) { if (object == BufferScanLimit(buffer) && BufferScanLimit(buffer) != BufferLimit(buffer)) { /* skip over buffered area */ @@ -1275,7 +1201,7 @@ static void AWLWalk(Pool pool, Seg seg, FormattedObjectsVisitor f, /* either before the buffer, or after it, never in it */ AVER(object < BufferGetInit(buffer) || BufferLimit(buffer) <= object); } - i = awlIndexOfAddr(base, awl, object); + i = PoolIndexOfAddr(base, pool, object); if (!BTGet(awlseg->alloc, i)) { /* This grain is free */ object = AddrAdd(object, PoolAlignment(pool)); @@ -1293,66 +1219,46 @@ static void AWLWalk(Pool pool, Seg seg, FormattedObjectsVisitor f, /* AWLTotalSize -- total memory allocated from the arena */ +/* TODO: This code is repeated in AMS */ static Size AWLTotalSize(Pool pool) { - AWL awl; - - AVERT(Pool, pool); - awl = PoolAWL(pool); - AVERT(AWL, awl); - - return awl->pgen.totalSize; + AWL awl = MustBeA(AWLPool, pool); + return awl->pgen->totalSize; } /* AWLFreeSize -- free memory (unused by client program) */ +/* TODO: This code is repeated in AMS */ static Size AWLFreeSize(Pool pool) { - AWL awl; - - AVERT(Pool, pool); - awl = PoolAWL(pool); - AVERT(AWL, awl); - - return awl->pgen.freeSize; + AWL awl = MustBeA(AWLPool, pool); + return awl->pgen->freeSize; } /* AWLPoolClass -- the class definition */ -DEFINE_POOL_CLASS(AWLPoolClass, this) +DEFINE_CLASS(Pool, AWLPool, klass) { - INHERIT_CLASS(this, AbstractCollectPoolClass); - PoolClassMixInFormat(this); - this->name = "AWL"; - this->size = sizeof(AWLStruct); - this->offset = offsetof(AWLStruct, poolStruct); - this->varargs = AWLVarargs; - this->init = AWLInit; - this->finish = AWLFinish; - this->bufferClass = RankBufClassGet; - this->bufferFill = AWLBufferFill; - this->bufferEmpty = AWLBufferEmpty; - this->access = AWLAccess; - this->whiten = AWLWhiten; - this->grey = AWLGrey; - this->blacken = AWLBlacken; - this->scan = AWLScan; - this->fix = AWLFix; - this->fixEmergency = AWLFix; - this->reclaim = AWLReclaim; - this->walk = AWLWalk; - this->totalSize = AWLTotalSize; - this->freeSize = AWLFreeSize; - AVERT(PoolClass, this); + INHERIT_CLASS(klass, AWLPool, AbstractCollectPool); + klass->instClassStruct.finish = AWLFinish; + klass->size = sizeof(AWLPoolStruct); + klass->varargs = AWLVarargs; + klass->init = AWLInit; + klass->bufferClass = RankBufClassGet; + klass->bufferFill = awlBufferFill; + klass->segPoolGen = awlSegPoolGen; + klass->totalSize = AWLTotalSize; + klass->freeSize = AWLFreeSize; + AVERT(PoolClass, klass); } mps_pool_class_t mps_class_awl(void) { - return (mps_pool_class_t)AWLPoolClassGet(); + return (mps_pool_class_t)CLASS(AWLPool); } @@ -1362,9 +1268,10 @@ ATTRIBUTE_UNUSED static Bool AWLCheck(AWL awl) { CHECKS(AWL, awl); - CHECKD(Pool, AWLPool(awl)); - CHECKL(AWLPool(awl)->class == AWLPoolClassGet()); - CHECKL(AWLGrainsSize(awl, (Count)1) == PoolAlignment(AWLPool(awl))); + CHECKC(AWLPool, awl); + CHECKD(Pool, CouldBeA(Pool, awl)); + if (awl->pgen != NULL) + CHECKD(PoolGen, awl->pgen); /* Nothing to check about succAccesses. */ CHECKL(FUNCHECK(awl->findDependent)); /* Don't bother to check stats. */ @@ -1374,7 +1281,7 @@ static Bool AWLCheck(AWL awl) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2016 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/poollo.c b/mps/code/poollo.c index a1a3f328468..27b4320c327 100644 --- a/mps/code/poollo.c +++ b/mps/code/poollo.c @@ -1,7 +1,7 @@ /* poollo.c: LEAF POOL CLASS * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * * DESIGN * @@ -23,21 +23,29 @@ typedef struct LOStruct *LO; typedef struct LOStruct { PoolStruct poolStruct; /* generic pool structure */ - Shift alignShift; /* log_2 of pool alignment */ - PoolGenStruct pgen; /* generation representing the pool */ - Sig sig; + PoolGenStruct pgenStruct; /* generation representing the pool */ + PoolGen pgen; /* NULL or pointer to pgenStruct */ + Sig sig; /* */ } LOStruct; -#define PoolPoolLO(pool) PARENT(LOStruct, poolStruct, pool) -#define LOPool(lo) (&(lo)->poolStruct) -#define LOGrainsSize(lo, grains) ((grains) << (lo)->alignShift) +typedef LO LOPool; +#define LOPoolCheck LOCheck +DECLARE_CLASS(Pool, LOPool, AbstractCollectPool); +DECLARE_CLASS(Seg, LOSeg, MutatorSeg); /* forward declaration */ static Bool LOCheck(LO lo); -/* LOGSegStruct -- LO segment structure */ +/* LOGSegStruct -- LO segment structure + * + * Colour is represented as follows: + * Black: +alloc +mark + * White: +alloc -mark + * Grey: objects have no references so can't be grey + * Free: -alloc ?mark + */ typedef struct LOSegStruct *LOSeg; @@ -45,37 +53,47 @@ typedef struct LOSegStruct *LOSeg; typedef struct LOSegStruct { GCSegStruct gcSegStruct; /* superclass fields must come first */ - LO lo; /* owning LO */ BT mark; /* mark bit table */ BT alloc; /* alloc bit table */ Count freeGrains; /* free grains */ - Count oldGrains; /* grains allocated prior to last collection */ + Count bufferedGrains; /* grains in buffers */ Count newGrains; /* grains allocated since last collection */ + Count oldGrains; /* grains allocated prior to last collection */ Sig sig; /* */ } LOSegStruct; -#define SegLOSeg(seg) ((LOSeg)(seg)) -#define LOSegSeg(loseg) ((Seg)(loseg)) - /* forward decls */ -static Res loSegInit(Seg seg, Pool pool, Addr base, Size size, - Bool reservoirPermit, ArgList args); -static void loSegFinish(Seg seg); +static Res loSegInit(Seg seg, Pool pool, Addr base, Size size, ArgList args); +static void loSegFinish(Inst inst); static Count loSegGrains(LOSeg loseg); +static Bool loSegBufferFill(Addr *baseReturn, Addr *limitReturn, + Seg seg, Size size, RankSet rankSet); +static void loSegBufferEmpty(Seg seg, Buffer buffer); +static Res loSegWhiten(Seg seg, Trace trace); +static Res loSegFix(Seg seg, ScanState ss, Ref *refIO); +static void loSegReclaim(Seg seg, Trace trace); +static void loSegWalk(Seg seg, Format format, FormattedObjectsVisitor f, + void *p, size_t s); /* LOSegClass -- Class definition for LO segments */ -DEFINE_SEG_CLASS(LOSegClass, class) +DEFINE_CLASS(Seg, LOSeg, klass) { - INHERIT_CLASS(class, GCSegClass); - SegClassMixInNoSplitMerge(class); - class->name = "LOSEG"; - class->size = sizeof(LOSegStruct); - class->init = loSegInit; - class->finish = loSegFinish; - AVERT(SegClass, class); + INHERIT_CLASS(klass, LOSeg, MutatorSeg); + SegClassMixInNoSplitMerge(klass); + klass->instClassStruct.finish = loSegFinish; + klass->size = sizeof(LOSegStruct); + klass->init = loSegInit; + klass->bufferFill = loSegBufferFill; + klass->bufferEmpty = loSegBufferEmpty; + klass->whiten = loSegWhiten; + klass->fix = loSegFix; + klass->fixEmergency = loSegFix; + klass->reclaim = loSegReclaim; + klass->walk = loSegWalk; + AVERT(SegClass, klass); } @@ -84,224 +102,197 @@ DEFINE_SEG_CLASS(LOSegClass, class) ATTRIBUTE_UNUSED static Bool LOSegCheck(LOSeg loseg) { + Seg seg = MustBeA(Seg, loseg); + Pool pool = SegPool(seg); CHECKS(LOSeg, loseg); 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->freeGrains + loseg->oldGrains + loseg->newGrains - == SegSize(LOSegSeg(loseg)) >> loseg->lo->alignShift); + CHECKL(loseg->freeGrains + loseg->bufferedGrains + loseg->newGrains + + loseg->oldGrains + == PoolSizeGrains(pool, SegSize(seg))); return TRUE; } /* loSegInit -- Init method for LO segments */ -static Res loSegInit(Seg seg, Pool pool, Addr base, Size size, - Bool reservoirPermit, ArgList args) +static Res loSegInit(Seg seg, Pool pool, Addr base, Size size, ArgList args) { - SegClass super; LOSeg loseg; - LO lo; Res res; - Size tablebytes; /* # bytes in each control array */ - Arena arena; + Size tableSize; /* # bytes in each control array */ + Arena arena = PoolArena(pool); /* number of bits needed in each control array */ Count grains; void *p; - AVERT(Seg, seg); - loseg = SegLOSeg(seg); - AVERT(Pool, pool); - arena = PoolArena(pool); - /* no useful checks for base and size */ - AVERT(Bool, reservoirPermit); - lo = PoolPoolLO(pool); - AVERT(LO, lo); - /* Initialize the superclass fields first via next-method call */ - super = SEG_SUPERCLASS(LOSegClass); - res = super->init(seg, pool, base, size, reservoirPermit, args); + res = NextMethod(Seg, LOSeg, init)(seg, pool, base, size, args); if(res != ResOK) - return res; + goto failSuperInit; + loseg = CouldBeA(LOSeg, seg); AVER(SegWhite(seg) == TraceSetEMPTY); - grains = size >> lo->alignShift; - tablebytes = BTSize(grains); - res = ControlAlloc(&p, arena, tablebytes, reservoirPermit); + grains = PoolSizeGrains(pool, size); + tableSize = BTSize(grains); + res = ControlAlloc(&p, arena, 2 * tableSize); if(res != ResOK) - goto failMarkTable; + goto failControlAlloc; loseg->mark = p; - res = ControlAlloc(&p, arena, tablebytes, reservoirPermit); - if(res != ResOK) - goto failAllocTable; - loseg->alloc = p; + loseg->alloc = PointerAdd(p, tableSize); BTResRange(loseg->alloc, 0, grains); - BTSetRange(loseg->mark, 0, grains); - loseg->lo = lo; loseg->freeGrains = grains; - loseg->oldGrains = (Count)0; + loseg->bufferedGrains = (Count)0; loseg->newGrains = (Count)0; + loseg->oldGrains = (Count)0; + + SetClassOfPoly(seg, CLASS(LOSeg)); loseg->sig = LOSegSig; - AVERT(LOSeg, loseg); + AVERC(LOSeg, loseg); + return ResOK; -failAllocTable: - ControlFree(arena, loseg->mark, tablebytes); -failMarkTable: - super->finish(seg); +failControlAlloc: + NextMethod(Inst, LOSeg, finish)(MustBeA(Inst, seg)); +failSuperInit: + AVER(res != ResOK); return res; } /* loSegFinish -- Finish method for LO segments */ -static void loSegFinish(Seg seg) +static void loSegFinish(Inst inst) { - LO lo; - LOSeg loseg; - SegClass super; - Pool pool; - Arena arena; + Seg seg = MustBeA(Seg, inst); + LOSeg loseg = MustBeA(LOSeg, seg); + Pool pool = SegPool(seg); + Arena arena = PoolArena(pool); Size tablesize; Count grains; - AVERT(Seg, seg); - loseg = SegLOSeg(seg); - AVERT(LOSeg, loseg); - pool = SegPool(seg); - lo = PoolPoolLO(pool); - AVERT(LO, lo); - arena = PoolArena(pool); + loseg->sig = SigInvalid; grains = loSegGrains(loseg); tablesize = BTSize(grains); - ControlFree(arena, (Addr)loseg->alloc, tablesize); - ControlFree(arena, (Addr)loseg->mark, tablesize); - loseg->sig = SigInvalid; + ControlFree(arena, loseg->mark, 2 * tablesize); - /* finish the superclass fields last */ - super = SEG_SUPERCLASS(LOSegClass); - super->finish(seg); + NextMethod(Inst, LOSeg, finish)(inst); } ATTRIBUTE_UNUSED static Count loSegGrains(LOSeg loseg) { - LO lo; - Size size; - - AVERT(LOSeg, loseg); - - lo = loseg->lo; - AVERT(LO, lo); - size = SegSize(LOSegSeg(loseg)); - return size >> lo->alignShift; + Seg seg = MustBeA(Seg, loseg); + Pool pool = SegPool(seg); + Size size = SegSize(seg); + return PoolSizeGrains(pool, size); } -/* Conversion between indexes and Addrs */ -#define loIndexOfAddr(base, lo, p) \ - (AddrOffset((base), (p)) >> (lo)->alignShift) +/* loSegBufferFill -- try filling buffer from segment */ -#define loAddrOfIndex(base, lo, i) \ - (AddrAdd((base), LOGrainsSize((lo), (i)))) - - -/* loSegFree -- mark block from baseIndex to limitIndex free */ - -static void loSegFree(LOSeg loseg, Index baseIndex, Index limitIndex) -{ - AVERT(LOSeg, loseg); - AVER(baseIndex < limitIndex); - AVER(limitIndex <= loSegGrains(loseg)); - - AVER(BTIsSetRange(loseg->alloc, baseIndex, limitIndex)); - BTResRange(loseg->alloc, baseIndex, limitIndex); - BTSetRange(loseg->mark, baseIndex, limitIndex); -} - - -/* Find a free block of size size in the segment. - * Return pointer to base and limit of block (which may be - * bigger than the requested size to accommodate buffering). - */ -static Bool loSegFindFree(Addr *bReturn, Addr *lReturn, - LOSeg loseg, Size size) +static Bool loSegBufferFill(Addr *baseReturn, Addr *limitReturn, + Seg seg, Size size, RankSet rankSet) { Index baseIndex, limitIndex; - LO lo; - Seg seg; - Count agrains; - Count grains; - Addr segBase; + LOSeg loseg = MustBeA_CRITICAL(LOSeg, seg); + Pool pool = SegPool(seg); + Count requestedGrains, segGrains, allocatedGrains; + Addr segBase, base, limit; - AVER(bReturn != NULL); - AVER(lReturn != NULL); - AVERT(LOSeg, loseg); + AVER_CRITICAL(baseReturn != NULL); + AVER_CRITICAL(limitReturn != NULL); + AVER_CRITICAL(SizeIsAligned(size, PoolAlignment(pool))); + AVER_CRITICAL(size > 0); + AVER_CRITICAL(rankSet == RankSetEMPTY); - lo = loseg->lo; - seg = LOSegSeg(loseg); - AVER(SizeIsAligned(size, LOPool(lo)->alignment)); + requestedGrains = PoolSizeGrains(pool, size); + if (loseg->freeGrains < requestedGrains) + /* Not enough space to satisfy the request. */ + return FALSE; - /* agrains is the number of grains corresponding to the size */ - /* of the allocation request */ - agrains = size >> lo->alignShift; - AVER(agrains >= 1); - AVER(agrains <= loseg->freeGrains); - AVER(size <= SegSize(seg)); - - if(SegBuffer(seg) != NULL) + if (SegHasBuffer(seg)) /* Don't bother trying to allocate from a buffered segment */ return FALSE; - grains = loSegGrains(loseg); - if(!BTFindLongResRange(&baseIndex, &limitIndex, loseg->alloc, - 0, grains, agrains)) { - return FALSE; + segGrains = PoolSizeGrains(pool, SegSize(seg)); + if (loseg->freeGrains == segGrains) { + /* Whole segment is free: no need for a search. */ + baseIndex = 0; + limitIndex = segGrains; + goto found; } - /* check that BTFindLongResRange really did find enough space */ - AVER(baseIndex < limitIndex); - AVER(LOGrainsSize(lo, limitIndex - baseIndex) >= size); - segBase = SegBase(seg); - *bReturn = loAddrOfIndex(segBase, lo, baseIndex); - *lReturn = loAddrOfIndex(segBase, lo, limitIndex); + if (!BTFindLongResRange(&baseIndex, &limitIndex, loseg->alloc, + 0, segGrains, requestedGrains)) + return FALSE; +found: + AVER(baseIndex < limitIndex); + allocatedGrains = limitIndex - baseIndex; + AVER(requestedGrains <= allocatedGrains); + AVER(BTIsResRange(loseg->alloc, baseIndex, limitIndex)); + /* Objects are allocated black. */ + /* TODO: This should depend on trace phase. */ + BTSetRange(loseg->alloc, baseIndex, limitIndex); + BTSetRange(loseg->mark, baseIndex, limitIndex); + AVER(loseg->freeGrains >= allocatedGrains); + loseg->freeGrains -= allocatedGrains; + loseg->bufferedGrains += allocatedGrains; + + segBase = SegBase(seg); + base = PoolAddrOfIndex(segBase, pool, baseIndex); + limit = PoolAddrOfIndex(segBase, pool, limitIndex); + PoolGenAccountForFill(PoolSegPoolGen(pool, seg), AddrOffset(base, limit)); + + *baseReturn = base; + *limitReturn = limit; return TRUE; } -/* loSegCreate -- Creates a segment of size at least size. - * - * Segments will be multiples of ArenaGrainSize. - */ +/* loSegBufferEmpty -- empty buffer to segment */ -static Res loSegCreate(LOSeg *loSegReturn, Pool pool, Size size, - Bool withReservoirPermit) +static void loSegBufferEmpty(Seg seg, Buffer buffer) { - LO lo; - Seg seg; - Res res; + LOSeg loseg = MustBeA(LOSeg, seg); + Pool pool = SegPool(seg); + Addr segBase, bufferBase, init, limit; + Index initIndex, limitIndex; + Count unusedGrains, usedGrains; - AVER(loSegReturn != NULL); - AVERT(Pool, pool); - AVER(size > 0); - AVERT(Bool, withReservoirPermit); - lo = PoolPoolLO(pool); - AVERT(LO, lo); + AVERT(Seg, seg); + AVERT(Buffer, buffer); + segBase = SegBase(seg); + bufferBase = BufferBase(buffer); + init = BufferGetInit(buffer); + limit = BufferLimit(buffer); + AVER(segBase <= bufferBase); + AVER(bufferBase <= init); + AVER(init <= limit); + AVER(limit <= SegLimit(seg)); - res = PoolGenAlloc(&seg, &lo->pgen, EnsureLOSegClass(), - SizeArenaGrains(size, PoolArena(pool)), - withReservoirPermit, argsNone); - if (res != ResOK) - return res; + initIndex = PoolIndexOfAddr(segBase, pool, init); + limitIndex = PoolIndexOfAddr(segBase, pool, limit); - *loSegReturn = SegLOSeg(seg); - return ResOK; + if (initIndex < limitIndex) + BTResRange(loseg->alloc, initIndex, limitIndex); + + unusedGrains = limitIndex - initIndex; + AVER(unusedGrains <= loseg->bufferedGrains); + usedGrains = loseg->bufferedGrains - unusedGrains; + loseg->freeGrains += unusedGrains; + loseg->bufferedGrains = 0; + loseg->newGrains += usedGrains; + + PoolGenAccountForEmpty(PoolSegPoolGen(pool, seg), + PoolGrainsSize(pool, usedGrains), + PoolGrainsSize(pool, unusedGrains), FALSE); } @@ -310,28 +301,27 @@ static Res loSegCreate(LOSeg *loSegReturn, Pool pool, Size size, * Could consider implementing this using Walk. */ -static void loSegReclaim(LOSeg loseg, Trace trace) +static void loSegReclaim(Seg seg, Trace trace) { + LOSeg loseg = MustBeA(LOSeg, seg); + Pool pool = SegPool(seg); + PoolGen pgen = PoolSegPoolGen(pool, seg); Addr p, base, limit; - Bool marked; + Buffer buffer; + Bool hasBuffer = SegBuffer(&buffer, seg); Count reclaimedGrains = (Count)0; - Seg seg; - LO lo; - Format format; + Format format = NULL; /* supress "may be used uninitialized" warning */ Count preservedInPlaceCount = (Count)0; Size preservedInPlaceSize = (Size)0; + Bool b; - AVERT(LOSeg, loseg); AVERT(Trace, trace); - seg = LOSegSeg(loseg); - lo = loseg->lo; base = SegBase(seg); limit = SegLimit(seg); - marked = FALSE; - format = LOPool(lo)->format; - AVERT(Format, format); + b = PoolFormat(&format, pool); + AVER(b); /* i is the index of the current pointer, * p is the actual address that is being considered. @@ -340,12 +330,10 @@ static void loSegReclaim(LOSeg loseg, Trace trace) */ p = base; while(p < limit) { - Buffer buffer = SegBuffer(seg); Addr q; Index i; - if(buffer != NULL) { - marked = TRUE; + if (hasBuffer) { if (p == BufferScanLimit(buffer) && BufferScanLimit(buffer) != BufferLimit(buffer)) { /* skip over buffered area */ @@ -356,22 +344,21 @@ static void loSegReclaim(LOSeg loseg, Trace trace) /* either before the buffer, or after it, never in it */ AVER(p < BufferGetInit(buffer) || BufferLimit(buffer) <= p); } - i = loIndexOfAddr(base, lo, p); + i = PoolIndexOfAddr(base, pool, p); if(!BTGet(loseg->alloc, i)) { /* This grain is free */ - p = AddrAdd(p, LOPool(lo)->alignment); + p = AddrAdd(p, pool->alignment); continue; } q = (*format->skip)(AddrAdd(p, format->headerSize)); q = AddrSub(q, format->headerSize); if(BTGet(loseg->mark, i)) { - marked = TRUE; ++preservedInPlaceCount; preservedInPlaceSize += AddrOffset(p, q); } else { - Index j = loIndexOfAddr(base, lo, q); + Index j = PoolIndexOfAddr(base, pool, q); /* This object is not marked, so free it */ - loSegFree(loseg, i, j); + BTResRange(loseg->alloc, i, j); reclaimedGrains += j - i; } p = q; @@ -382,47 +369,39 @@ static void loSegReclaim(LOSeg loseg, Trace trace) 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; + PoolGenAccountForReclaim(pgen, PoolGrainsSize(pool, reclaimedGrains), FALSE); + STATISTIC(trace->reclaimSize += PoolGrainsSize(pool, reclaimedGrains)); + STATISTIC(trace->preservedInPlaceCount += preservedInPlaceCount); + GenDescSurvived(pgen->gen, trace, 0, preservedInPlaceSize); SegSetWhite(seg, TraceSetDel(SegWhite(seg), trace)); - if (!marked) - PoolGenFree(&lo->pgen, seg, - LOGrainsSize(lo, loseg->freeGrains), - LOGrainsSize(lo, loseg->oldGrains), - LOGrainsSize(lo, loseg->newGrains), + if (loseg->freeGrains == PoolSizeGrains(pool, SegSize(seg)) && !hasBuffer) { + AVER(loseg->bufferedGrains == 0); + PoolGenFree(pgen, seg, + PoolGrainsSize(pool, loseg->freeGrains), + PoolGrainsSize(pool, loseg->oldGrains), + PoolGrainsSize(pool, loseg->newGrains), 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, FormattedObjectsVisitor f, - void *p, size_t s) +/* Walks over _all_ objects in the segnent: 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 loSegWalk(Seg seg, Format format, FormattedObjectsVisitor f, + void *p, size_t s) { Addr base; - LO lo; - LOSeg loseg; + LOSeg loseg = MustBeA(LOSeg, seg); + Pool pool = SegPool(seg); Index i, grains; - Format format; - AVERT(Pool, pool); - AVERT(Seg, seg); + AVERT(Format, format); AVER(FUNCHECK(f)); /* p and s are arbitrary closures and can't be checked */ - lo = PoolPoolLO(pool); - AVERT(LO, lo); - loseg = SegLOSeg(seg); - AVERT(LOSeg, loseg); - - format = pool->format; - AVERT(Format, format); - base = SegBase(seg); grains = loSegGrains(loseg); i = 0; @@ -430,17 +409,17 @@ static void LOWalk(Pool pool, Seg seg, FormattedObjectsVisitor f, while(i < grains) { /* object is a slight misnomer because it might point to a */ /* free grain */ - Addr object = loAddrOfIndex(base, lo, i); + Addr object = PoolAddrOfIndex(base, pool, i); Addr next; Index j; + Buffer buffer; - if(SegBuffer(seg) != NULL) { - Buffer buffer = SegBuffer(seg); + if (SegBuffer(&buffer, seg)) { if(object == BufferScanLimit(buffer) && BufferScanLimit(buffer) != BufferLimit(buffer)) { /* skip over buffered area */ object = BufferLimit(buffer); - i = loIndexOfAddr(base, lo, object); + i = PoolIndexOfAddr(base, pool, object); continue; } /* since we skip over the buffered area we are always */ @@ -455,7 +434,7 @@ static void LOWalk(Pool pool, Seg seg, FormattedObjectsVisitor f, object = AddrAdd(object, format->headerSize); next = (*format->skip)(object); next = AddrSub(next, format->headerSize); - j = loIndexOfAddr(base, lo, next); + j = PoolIndexOfAddr(base, pool, next); AVER(i < j); (*f)(object, pool->format, pool, p, s); i = j; @@ -476,24 +455,27 @@ static void LOVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) /* LOInit -- initialize an LO pool */ -static Res LOInit(Pool pool, ArgList args) +static Res LOInit(Pool pool, Arena arena, PoolClass klass, ArgList args) { LO lo; - Arena arena; Res res; ArgStruct arg; Chain chain; unsigned gen = LO_GEN_DEFAULT; - AVERT(Pool, pool); + AVER(pool != NULL); + AVERT(Arena, arena); AVERT(ArgList, args); + UNUSED(klass); /* used for debug pools only */ - arena = PoolArena(pool); - - lo = PoolPoolLO(pool); + res = NextMethod(Pool, LOPool, init)(pool, arena, klass, args); + if (res != ResOK) + goto failNextInit; + lo = CouldBeA(LOPool, pool); + + /* Ensure a format was supplied in the argument list. */ + AVER(pool->format != NULL); - ArgRequire(&arg, args, MPS_KEY_FORMAT); - pool->format = arg.val.format; if (ArgPick(&arg, args, MPS_KEY_CHAIN)) chain = arg.val.chain; else { @@ -510,18 +492,26 @@ static Res LOInit(Pool pool, ArgList args) AVER(chain->arena == arena); pool->alignment = pool->format->alignment; - lo->alignShift = SizeLog2((Size)PoolAlignment(pool)); + pool->alignShift = SizeLog2(pool->alignment); - res = PoolGenInit(&lo->pgen, ChainGen(chain, gen), pool); + lo->pgen = NULL; + + SetClassOfPoly(pool, CLASS(LOPool)); + lo->sig = LOSig; + AVERC(LOPool, lo); + + res = PoolGenInit(&lo->pgenStruct, ChainGen(chain, gen), pool); if (res != ResOK) goto failGenInit; + lo->pgen = &lo->pgenStruct; - lo->sig = LOSig; - AVERT(LO, lo); EVENT2(PoolInitLO, pool, pool->format); + return ResOK; failGenInit: + NextMethod(Inst, LOPool, finish)(MustBeA(Inst, pool)); +failNextInit: AVER(res != ResOK); return res; } @@ -529,323 +519,217 @@ static Res LOInit(Pool pool, ArgList args) /* LOFinish -- finish an LO pool */ -static void LOFinish(Pool pool) +static void LOFinish(Inst inst) { - LO lo; + Pool pool = MustBeA(AbstractPool, inst); + LO lo = MustBeA(LOPool, pool); Ring node, nextNode; - AVERT(Pool, pool); - lo = PoolPoolLO(pool); - AVERT(LO, lo); - RING_FOR(node, &pool->segRing, nextNode) { Seg seg = SegOfPoolRing(node); - LOSeg loseg = SegLOSeg(seg); + LOSeg loseg = MustBeA(LOSeg, seg); + AVER(!SegHasBuffer(seg)); AVERT(LOSeg, loseg); - PoolGenFree(&lo->pgen, seg, - LOGrainsSize(lo, loseg->freeGrains), - LOGrainsSize(lo, loseg->oldGrains), - LOGrainsSize(lo, loseg->newGrains), + AVER(loseg->bufferedGrains == 0); + PoolGenFree(lo->pgen, seg, + PoolGrainsSize(pool, loseg->freeGrains), + PoolGrainsSize(pool, loseg->oldGrains), + PoolGrainsSize(pool, loseg->newGrains), FALSE); } - PoolGenFinish(&lo->pgen); + PoolGenFinish(lo->pgen); lo->sig = SigInvalid; + + NextMethod(Inst, LOPool, finish)(inst); } static Res LOBufferFill(Addr *baseReturn, Addr *limitReturn, Pool pool, Buffer buffer, - Size size, Bool withReservoirPermit) + Size size) { + LO lo = MustBeA(LOPool, pool); Res res; Ring node, nextNode; - LO lo; - LOSeg loseg; - Addr base, limit; + RankSet rankSet; + Seg seg; + Bool b; AVER(baseReturn != NULL); AVER(limitReturn != NULL); - AVERT(Pool, pool); - lo = PARENT(LOStruct, poolStruct, pool); - AVERT(LO, lo); - AVERT(Buffer, buffer); + AVERC(Buffer, buffer); AVER(BufferIsReset(buffer)); AVER(BufferRankSet(buffer) == RankSetEMPTY); AVER(size > 0); AVER(SizeIsAligned(size, PoolAlignment(pool))); - 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(LOGrainsSize(lo, loseg->freeGrains) >= size - && loSegFindFree(&base, &limit, loseg, size)) - goto found; + rankSet = BufferRankSet(buffer); + RING_FOR(node, PoolSegRing(pool), nextNode) { + seg = SegOfPoolRing(node); + if (SegBufferFill(baseReturn, limitReturn, seg, size, rankSet)) + return ResOK; } /* No segment had enough space, so make a new one. */ - res = loSegCreate(&loseg, pool, size, withReservoirPermit); - if(res != ResOK) { - goto failCreate; - } - base = SegBase(LOSegSeg(loseg)); - limit = SegLimit(LOSegSeg(loseg)); - -found: - { - Index baseIndex, limitIndex; - Addr segBase; - - segBase = SegBase(LOSegSeg(loseg)); - /* mark the newly buffered region as allocated */ - baseIndex = loIndexOfAddr(segBase, lo, base); - limitIndex = loIndexOfAddr(segBase, lo, limit); - AVER(BTIsResRange(loseg->alloc, baseIndex, limitIndex)); - AVER(BTIsSetRange(loseg->mark, baseIndex, limitIndex)); - BTSetRange(loseg->alloc, baseIndex, limitIndex); - AVER(loseg->freeGrains >= limitIndex - baseIndex); - loseg->freeGrains -= limitIndex - baseIndex; - loseg->newGrains += limitIndex - baseIndex; - } - - PoolGenAccountForFill(&lo->pgen, AddrOffset(base, limit), FALSE); - - *baseReturn = base; - *limitReturn = limit; + res = PoolGenAlloc(&seg, lo->pgen, CLASS(LOSeg), + SizeArenaGrains(size, PoolArena(pool)), + argsNone); + if (res != ResOK) + return res; + b = SegBufferFill(baseReturn, limitReturn, seg, size, rankSet); + AVER(b); return ResOK; - -failCreate: - return res; } /* Synchronise the buffer with the alloc Bit Table in the segment. */ -static void LOBufferEmpty(Pool pool, Buffer buffer, Addr init, Addr limit) + +/* loSegPoolGen -- get pool generation for an LO segment */ + +static PoolGen loSegPoolGen(Pool pool, Seg seg) { - LO lo; - Addr base, segBase; - Seg seg; - LOSeg loseg; - Index initIndex, limitIndex; - - AVERT(Pool, pool); - lo = PARENT(LOStruct, poolStruct, pool); - AVERT(LO, lo); - AVERT(Buffer, buffer); - AVER(BufferIsReady(buffer)); - seg = BufferSeg(buffer); + LO lo = MustBeA(LOPool, pool); AVERT(Seg, seg); - AVER(init <= limit); - - loseg = SegLOSeg(seg); - AVERT(LOSeg, loseg); - AVER(loseg->lo == lo); - - base = BufferBase(buffer); - segBase = SegBase(seg); - - AVER(AddrIsAligned(base, PoolAlignment(pool))); - AVER(segBase <= base); - AVER(base < SegLimit(seg)); - AVER(segBase <= init); - AVER(init <= SegLimit(seg)); - - /* convert base, init, and limit, to quantum positions */ - initIndex = loIndexOfAddr(segBase, lo, init); - limitIndex = loIndexOfAddr(segBase, lo, limit); - - if(initIndex != limitIndex) { - /* Free the unused portion of the buffer (this must be "new", since - * it's not condemned). */ - loSegFree(loseg, initIndex, limitIndex); - AVER(loseg->newGrains >= limitIndex - initIndex); - loseg->newGrains -= limitIndex - initIndex; - loseg->freeGrains += limitIndex - initIndex; - PoolGenAccountForEmpty(&lo->pgen, AddrOffset(init, limit), FALSE); - } + return lo->pgen; } -/* LOWhiten -- whiten a segment */ +/* loSegWhiten -- whiten a segment */ -static Res LOWhiten(Pool pool, Trace trace, Seg seg) +static Res loSegWhiten(Seg seg, Trace trace) { - LO lo; - LOSeg loseg; + LOSeg loseg = MustBeA(LOSeg, seg); + Pool pool = SegPool(seg); + PoolGen pgen = PoolSegPoolGen(pool, seg); Buffer buffer; - Count grains, uncondemned; - - AVERT(Pool, pool); - lo = PoolPoolLO(pool); - AVERT(LO, lo); + Count grains, agedGrains, uncondemnedGrains; AVERT(Trace, trace); - AVERT(Seg, seg); AVER(SegWhite(seg) == TraceSetEMPTY); - loseg = SegLOSeg(seg); - AVERT(LOSeg, loseg); grains = loSegGrains(loseg); /* Whiten allocated objects; leave free areas black. */ - buffer = SegBuffer(seg); - if (buffer != NULL) { + if (SegBuffer(&buffer, seg)) { Addr base = SegBase(seg); - Index scanLimitIndex = loIndexOfAddr(base, lo, BufferScanLimit(buffer)); - Index limitIndex = loIndexOfAddr(base, lo, BufferLimit(buffer)); - uncondemned = limitIndex - scanLimitIndex; + Index scanLimitIndex = PoolIndexOfAddr(base, pool, BufferScanLimit(buffer)); + Index limitIndex = PoolIndexOfAddr(base, pool, BufferLimit(buffer)); + uncondemnedGrains = 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; + uncondemnedGrains = (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)); + /* The unused part of the buffer remains buffered: the rest becomes old. */ + AVER(loseg->bufferedGrains >= uncondemnedGrains); + agedGrains = loseg->bufferedGrains - uncondemnedGrains; + PoolGenAccountForAge(pgen, PoolGrainsSize(pool, agedGrains), + PoolGrainsSize(pool, loseg->newGrains), FALSE); + loseg->oldGrains += agedGrains + loseg->newGrains; + loseg->bufferedGrains = uncondemnedGrains; + loseg->newGrains = 0; + + if (loseg->oldGrains > 0) { + GenDescCondemned(pgen->gen, trace, + PoolGrainsSize(pool, loseg->oldGrains)); + SegSetWhite(seg, TraceSetAdd(SegWhite(seg), trace)); + } return ResOK; } -static Res LOFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) +static Res loSegFix(Seg seg, ScanState ss, Ref *refIO) { - LO lo; - LOSeg loseg; + LOSeg loseg = MustBeA_CRITICAL(LOSeg, seg); + Pool pool = SegPool(seg); Ref clientRef; Addr base; + Index i; - AVERT_CRITICAL(Pool, pool); AVERT_CRITICAL(ScanState, ss); - AVERT_CRITICAL(Seg, seg); AVER_CRITICAL(TraceSetInter(SegWhite(seg), ss->traces) != TraceSetEMPTY); AVER_CRITICAL(refIO != NULL); - lo = PARENT(LOStruct, poolStruct, pool); - AVERT_CRITICAL(LO, lo); - loseg = SegLOSeg(seg); - AVERT_CRITICAL(LOSeg, loseg); - - ss->wasMarked = TRUE; /* */ clientRef = *refIO; base = AddrSub((Addr)clientRef, pool->format->headerSize); - /* can get an ambiguous reference to 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. */ + + /* Not a real reference if out of bounds. This can happen if an + ambiguous reference is closer to the base of the segment than the + header size. */ if (base < SegBase(seg)) { + AVER(ss->rank == RankAMBIG); return ResOK; } - switch(ss->rank) { - case RankAMBIG: - if(!AddrIsAligned(base, PoolAlignment(pool))) { - return ResOK; + /* Not a real reference if unaligned. */ + if (!AddrIsAligned(base, PoolAlignment(pool))) { + AVER(ss->rank == RankAMBIG); + return ResOK; + } + + i = PoolIndexOfAddr(SegBase(seg), pool, base); + + /* Not a real reference if unallocated. */ + if (!BTGet(loseg->alloc, i)) { + AVER(ss->rank == RankAMBIG); + return ResOK; + } + + if(!BTGet(loseg->mark, i)) { + ss->wasMarked = FALSE; /* */ + if(ss->rank == RankWEAK) { + *refIO = (Addr)0; + } else { + BTSet(loseg->mark, i); } - /* fall through */ - - case RankEXACT: - case RankFINAL: - case RankWEAK: { - Size i = AddrOffset(SegBase(seg), base) >> lo->alignShift; - - if(!BTGet(loseg->mark, i)) { - ss->wasMarked = FALSE; /* */ - if(ss->rank == RankWEAK) { - *refIO = (Addr)0; - } else { - BTSet(loseg->mark, i); - } - } - } break; - - default: - NOTREACHED; - break; } return ResOK; } -static void LOReclaim(Pool pool, Trace trace, Seg seg) -{ - LO lo; - LOSeg loseg; - - AVERT(Pool, pool); - lo = PoolPoolLO(pool); - AVERT(LO, lo); - - AVERT(Trace, trace); - AVERT(Seg, seg); - AVER(TraceSetIsMember(SegWhite(seg), trace)); - - loseg = SegLOSeg(seg); - loSegReclaim(loseg, trace); -} - - /* LOTotalSize -- total memory allocated from the arena */ +/* TODO: This code is repeated in AMS */ static Size LOTotalSize(Pool pool) { - LO lo; - - AVERT(Pool, pool); - lo = PoolPoolLO(pool); - AVERT(LO, lo); - - return lo->pgen.totalSize; + LO lo = MustBeA(LOPool, pool); + return lo->pgen->totalSize; } /* LOFreeSize -- free memory (unused by client program) */ +/* TODO: This code is repeated in AMS */ static Size LOFreeSize(Pool pool) { - LO lo; - - AVERT(Pool, pool); - lo = PoolPoolLO(pool); - AVERT(LO, lo); - - return lo->pgen.freeSize; + LO lo = MustBeA(LOPool, pool); + return lo->pgen->freeSize; } /* LOPoolClass -- the class definition */ -DEFINE_POOL_CLASS(LOPoolClass, this) +DEFINE_CLASS(Pool, LOPool, klass) { - INHERIT_CLASS(this, AbstractSegBufPoolClass); - PoolClassMixInFormat(this); - PoolClassMixInCollect(this); - this->name = "LO"; - this->size = sizeof(LOStruct); - this->offset = offsetof(LOStruct, poolStruct); - this->varargs = LOVarargs; - this->init = LOInit; - this->finish = LOFinish; - this->bufferFill = LOBufferFill; - this->bufferEmpty = LOBufferEmpty; - this->whiten = LOWhiten; - this->fix = LOFix; - this->fixEmergency = LOFix; - this->reclaim = LOReclaim; - this->walk = LOWalk; - this->totalSize = LOTotalSize; - this->freeSize = LOFreeSize; - AVERT(PoolClass, this); + INHERIT_CLASS(klass, LOPool, AbstractCollectPool); + klass->instClassStruct.finish = LOFinish; + klass->size = sizeof(LOStruct); + klass->varargs = LOVarargs; + klass->init = LOInit; + klass->bufferFill = LOBufferFill; + klass->segPoolGen = loSegPoolGen; + klass->totalSize = LOTotalSize; + klass->freeSize = LOFreeSize; + AVERT(PoolClass, klass); } @@ -853,7 +737,7 @@ DEFINE_POOL_CLASS(LOPoolClass, this) mps_pool_class_t mps_class_lo(void) { - return (mps_pool_class_t)EnsureLOPoolClass(); + return (mps_pool_class_t)CLASS(LOPool); } @@ -863,18 +747,20 @@ ATTRIBUTE_UNUSED static Bool LOCheck(LO lo) { CHECKS(LO, lo); - CHECKD(Pool, LOPool(lo)); - CHECKL(LOPool(lo)->class == EnsureLOPoolClass()); - CHECKL(ShiftCheck(lo->alignShift)); - CHECKL(LOGrainsSize(lo, (Count)1) == PoolAlignment(LOPool(lo))); - CHECKD(PoolGen, &lo->pgen); + CHECKC(LOPool, lo); + CHECKD(Pool, &lo->poolStruct); + CHECKC(LOPool, lo); + if (lo->pgen != NULL) { + CHECKL(lo->pgen == &lo->pgenStruct); + CHECKD(PoolGen, lo->pgen); + } return TRUE; } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/poolmfs.c b/mps/code/poolmfs.c index 90232621c25..9123a44b9af 100644 --- a/mps/code/poolmfs.c +++ b/mps/code/poolmfs.c @@ -1,21 +1,11 @@ /* poolmfs.c: MANUAL FIXED SMALL UNIT POOL * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. * * This is the implementation of the MFS pool class. * - * DESIGN - * - * .design.misplaced: This design is misplaced, it should be in a - * separate document. - * - * MFS operates in a very simple manner: each region allocated from - * the arena is divided into units. Free units are kept on a linked - * list using a header stored in the unit itself. The linked list is - * not ordered; allocation anddeallocation simply pop and push from - * the head of the list. This is fast, but successive allocations might - * have poor locality if previous successive frees did. + * See design.mps.poolmfs. * * .restriction: This pool cannot allocate from the arena control * pool (as the control pool is an instance of PoolClassMV and MV uses @@ -47,9 +37,6 @@ SRCID(poolmfs, "$Id$"); #define ROUND(unit, n) ((n)+(unit)-1 - ((n)+(unit)-1)%(unit)) -#define PoolPoolMFS(pool) PARENT(MFSStruct, poolStruct, pool) - - /* HeaderStruct -- Freelist structure */ typedef struct MFSHeaderStruct { @@ -75,17 +62,19 @@ static void MFSVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) ARG_DEFINE_KEY(MFS_UNIT_SIZE, Size); ARG_DEFINE_KEY(MFSExtendSelf, Bool); -static Res MFSInit(Pool pool, ArgList args) +static Res MFSInit(Pool pool, Arena arena, PoolClass klass, ArgList args) { Size extendBy = MFS_EXTEND_BY_DEFAULT; Bool extendSelf = TRUE; - Size unitSize; + Size unitSize, ringSize, minExtendBy; MFS mfs; - Arena arena; ArgStruct arg; + Res res; AVER(pool != NULL); + AVERT(Arena, arena); AVERT(ArgList, args); + UNUSED(klass); /* used for debug pools only */ ArgRequire(&arg, args, MPS_KEY_MFS_UNIT_SIZE); unitSize = arg.val.size; @@ -97,101 +86,117 @@ static Res MFSInit(Pool pool, ArgList args) AVER(unitSize > 0); AVER(extendBy > 0); AVERT(Bool, extendSelf); - - mfs = PoolPoolMFS(pool); - arena = PoolArena(pool); + + res = NextMethod(Pool, MFSPool, init)(pool, arena, klass, args); + if (res != ResOK) + goto failNextInit; + mfs = CouldBeA(MFSPool, pool); mfs->unroundedUnitSize = unitSize; if (unitSize < UNIT_MIN) unitSize = UNIT_MIN; unitSize = SizeAlignUp(unitSize, MPS_PF_ALIGN); - if (extendBy < unitSize) - extendBy = unitSize; + ringSize = SizeAlignUp(sizeof(RingStruct), MPS_PF_ALIGN); + minExtendBy = ringSize + unitSize; + if (extendBy < minExtendBy) + extendBy = minExtendBy; + extendBy = SizeArenaGrains(extendBy, arena); mfs->extendBy = extendBy; mfs->extendSelf = extendSelf; mfs->unitSize = unitSize; mfs->freeList = NULL; - mfs->tractList = NULL; + RingInit(&mfs->extentRing); mfs->total = 0; mfs->free = 0; - mfs->sig = MFSSig; - AVERT(MFS, mfs); + SetClassOfPoly(pool, CLASS(MFSPool)); + mfs->sig = MFSSig; + AVERC(MFS, mfs); + EVENT5(PoolInitMFS, pool, arena, extendBy, BOOLOF(extendSelf), unitSize); return ResOK; + +failNextInit: + AVER(res != ResOK); + return res; } -void MFSFinishTracts(Pool pool, MFSTractVisitor visitor, - void *closureP, Size closureS) +void MFSFinishExtents(Pool pool, MFSExtentVisitor visitor, + void *closure) { - MFS mfs; + MFS mfs = MustBeA(MFSPool, pool); + Ring ring, node, next; - AVERT(Pool, pool); - mfs = PoolPoolMFS(pool); - AVERT(MFS, mfs); - - while (mfs->tractList != NULL) { - Tract nextTract = (Tract)TractP(mfs->tractList); /* .tract.chain */ - visitor(pool, TractBase(mfs->tractList), mfs->extendBy, closureP, closureS); - mfs->tractList = nextTract; + AVER(FUNCHECK(visitor)); + /* Can't check closure */ + + ring = &mfs->extentRing; + node = RingNext(ring); + RING_FOR(node, ring, next) { + Addr base = (Addr)node; /* See .ring-node.at-base. */ + RingRemove(node); + visitor(pool, base, mfs->extendBy, closure); } } -static void MFSTractFreeVisitor(Pool pool, Addr base, Size size, - void *closureP, Size closureS) +static void MFSExtentFreeVisitor(Pool pool, Addr base, Size size, + void *closure) { - AVER(closureP == UNUSED_POINTER); - AVER(closureS == UNUSED_SIZE); - UNUSED(closureP); - UNUSED(closureS); + AVER(closure == UNUSED_POINTER); + UNUSED(closure); ArenaFree(base, size, pool); } -static void MFSFinish(Pool pool) +static void MFSFinish(Inst inst) { - MFS mfs; + Pool pool = MustBeA(AbstractPool, inst); + MFS mfs = MustBeA(MFSPool, pool); - AVERT(Pool, pool); - mfs = PoolPoolMFS(pool); - AVERT(MFS, mfs); - - MFSFinishTracts(pool, MFSTractFreeVisitor, UNUSED_POINTER, UNUSED_SIZE); + MFSFinishExtents(pool, MFSExtentFreeVisitor, UNUSED_POINTER); mfs->sig = SigInvalid; + + NextMethod(Inst, MFSPool, finish)(inst); } -void MFSExtend(Pool pool, Addr base, Size size) +void MFSExtend(Pool pool, Addr base, Addr limit) { - MFS mfs; - Tract tract; + MFS mfs = MustBeA(MFSPool, pool); Word i, unitsPerExtent; + Size size; Size unitSize; + Size ringSize; Header header = NULL; + Ring mfsRing; - AVERT(Pool, pool); - mfs = PoolPoolMFS(pool); - AVERT(MFS, mfs); - AVER(size == mfs->extendBy); + AVER(base < limit); + AVER(AddrOffset(base, limit) == 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)); - - /* .tract.chain: chain first tracts through TractP(tract) */ - tract = TractOfBaseAddr(PoolArena(pool), base); - AVER(TractPool(tract) == pool); + /* .ring-node.at-base: Store the extent ring node at the base of the + extent. This transgresses the rule that pools should allocate + control structures from another pool, because an MFS is required + during bootstrap when no other pools are available. See + */ + mfsRing = (Ring)base; + RingInit(mfsRing); + RingAppend(&mfs->extentRing, mfsRing); - TractSetP(tract, (void *)mfs->tractList); - mfs->tractList = tract; + ringSize = SizeAlignUp(sizeof(RingStruct), MPS_PF_ALIGN); + base = AddrAdd(base, ringSize); + AVER(base < limit); + size = AddrOffset(base, limit); /* Update accounting */ mfs->total += size; @@ -227,20 +232,14 @@ void MFSExtend(Pool pool, Addr base, Size size) * arena. */ -static Res MFSAlloc(Addr *pReturn, Pool pool, Size size, - Bool withReservoirPermit) +static Res MFSAlloc(Addr *pReturn, Pool pool, Size size) { + MFS mfs = MustBeA(MFSPool, pool); Header f; Res res; - MFS mfs; - - AVERT(Pool, pool); - mfs = PoolPoolMFS(pool); - AVERT(MFS, mfs); AVER(pReturn != NULL); AVER(size == mfs->unroundedUnitSize); - AVERT(Bool, withReservoirPermit); f = mfs->freeList; @@ -254,13 +253,12 @@ static Res MFSAlloc(Addr *pReturn, Pool pool, Size size, if (!mfs->extendSelf) return ResLIMIT; - /* Create a new region and attach it to the pool. */ - res = ArenaAlloc(&base, LocusPrefDefault(), mfs->extendBy, pool, - withReservoirPermit); + /* Create a new extent and attach it to the pool. */ + res = ArenaAlloc(&base, LocusPrefDefault(), mfs->extendBy, pool); if(res != ResOK) return res; - MFSExtend(pool, base, mfs->extendBy); + MFSExtend(pool, base, AddrAdd(base, mfs->extendBy)); /* The first unit in the region is now the head of the new free list. */ f = mfs->freeList; @@ -287,12 +285,8 @@ static Res MFSAlloc(Addr *pReturn, Pool pool, Size size, static void MFSFree(Pool pool, Addr old, Size size) { + MFS mfs = MustBeA(MFSPool, pool); Header h; - MFS mfs; - - AVERT(Pool, pool); - mfs = PoolPoolMFS(pool); - AVERT(MFS, mfs); AVER(old != (Addr)0); AVER(size == mfs->unroundedUnitSize); @@ -309,12 +303,7 @@ static void MFSFree(Pool pool, Addr old, Size size) static Size MFSTotalSize(Pool pool) { - MFS mfs; - - AVERT(Pool, pool); - mfs = PoolPoolMFS(pool); - AVERT(MFS, mfs); - + MFS mfs = MustBeA(MFSPool, pool); return mfs->total; } @@ -323,65 +312,57 @@ static Size MFSTotalSize(Pool pool) static Size MFSFreeSize(Pool pool) { - MFS mfs; - - AVERT(Pool, pool); - mfs = PoolPoolMFS(pool); - AVERT(MFS, mfs); - + MFS mfs = MustBeA(MFSPool, pool); return mfs->free; } -static Res MFSDescribe(Pool pool, mps_lib_FILE *stream, Count depth) +static Res MFSDescribe(Inst inst, mps_lib_FILE *stream, Count depth) { - MFS mfs; + Pool pool = CouldBeA(AbstractPool, inst); + MFS mfs = CouldBeA(MFSPool, pool); Res res; - AVERT(Pool, pool); - mfs = PoolPoolMFS(pool); - AVERT(MFS, mfs); + if (!TESTC(MFSPool, mfs)) + return ResPARAM; + if (stream == NULL) + return ResPARAM; - AVER(stream != NULL); - - 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, - "tractList $P\n", (WriteFP)mfs->tractList, - NULL); + res = NextMethod(Inst, MFSPool, describe)(inst, stream, depth); if (res != ResOK) return res; - return ResOK; + return WriteF(stream, depth + 2, + "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, + NULL); } -DEFINE_POOL_CLASS(MFSPoolClass, this) +DEFINE_CLASS(Pool, MFSPool, klass) { - INHERIT_CLASS(this, AbstractPoolClass); - this->name = "MFS"; - this->size = sizeof(MFSStruct); - this->offset = offsetof(MFSStruct, poolStruct); - this->varargs = MFSVarargs; - this->init = MFSInit; - this->finish = MFSFinish; - this->alloc = MFSAlloc; - this->free = MFSFree; - this->totalSize = MFSTotalSize; - this->freeSize = MFSFreeSize; - this->describe = MFSDescribe; - AVERT(PoolClass, this); + INHERIT_CLASS(klass, MFSPool, AbstractPool); + klass->instClassStruct.describe = MFSDescribe; + klass->instClassStruct.finish = MFSFinish; + klass->size = sizeof(MFSStruct); + klass->varargs = MFSVarargs; + klass->init = MFSInit; + klass->alloc = MFSAlloc; + klass->free = MFSFree; + klass->totalSize = MFSTotalSize; + klass->freeSize = MFSFreeSize; + AVERT(PoolClass, klass); } PoolClass PoolClassMFS(void) { - return EnsureMFSPoolClass(); + return CLASS(MFSPool); } @@ -396,8 +377,9 @@ Bool MFSCheck(MFS mfs) Arena arena; CHECKS(MFS, mfs); + CHECKC(MFSPool, mfs); CHECKD(Pool, MFSPool(mfs)); - CHECKL(MFSPool(mfs)->class == EnsureMFSPoolClass()); + CHECKC(MFSPool, mfs); CHECKL(mfs->unitSize >= UNIT_MIN); CHECKL(mfs->extendBy >= UNIT_MIN); CHECKL(BoolCheck(mfs->extendSelf)); @@ -405,9 +387,7 @@ Bool MFSCheck(MFS mfs) CHECKL(SizeIsArenaGrains(mfs->extendBy, arena)); CHECKL(SizeAlignUp(mfs->unroundedUnitSize, PoolAlignment(MFSPool(mfs))) == mfs->unitSize); - if(mfs->tractList != NULL) { - CHECKD_NOSIG(Tract, mfs->tractList); - } + CHECKD_NOSIG(Ring, &mfs->extentRing); CHECKL(mfs->free <= mfs->total); CHECKL((mfs->total - mfs->free) % mfs->unitSize == 0); return TRUE; @@ -416,7 +396,7 @@ Bool MFSCheck(MFS mfs) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/poolmfs.h b/mps/code/poolmfs.h index ac4122a9f02..9f112ea80f5 100644 --- a/mps/code/poolmfs.h +++ b/mps/code/poolmfs.h @@ -2,7 +2,7 @@ * * $Id$ * - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 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 @@ -32,6 +32,8 @@ #include "mpscmfs.h" typedef struct MFSStruct *MFS; +typedef MFS MFSPool; +DECLARE_CLASS(Pool, MFSPool, AbstractPool); #define MFSPool(mfs) (&(mfs)->poolStruct) @@ -43,19 +45,19 @@ extern const struct mps_key_s _mps_key_MFSExtendSelf; #define MFSExtendSelf (&_mps_key_MFSExtendSelf) #define MFSExtendSelf_FIELD b -extern void MFSExtend(Pool pool, Addr base, Size size); +extern void MFSExtend(Pool pool, Addr base, Addr limit); -typedef void MFSTractVisitor(Pool pool, Addr base, Size size, - void *closureP, Size closureS); -extern void MFSFinishTracts(Pool pool, MFSTractVisitor visitor, - void *closureP, Size closureS); +typedef void MFSExtentVisitor(Pool pool, Addr base, Size size, + void *closure); +extern void MFSFinishExtents(Pool pool, MFSExtentVisitor visitor, + void *closure); #endif /* poolmfs_h */ /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/poolmrg.c b/mps/code/poolmrg.c index 2cb5e7a2f21..0fb05d23cc6 100644 --- a/mps/code/poolmrg.c +++ b/mps/code/poolmrg.c @@ -1,7 +1,7 @@ /* poolmrg.c: MANUAL RANK GUARDIAN POOL * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. * * @@ -74,7 +74,8 @@ typedef struct RefPartStruct { } RefPartStruct; -/* MRGRefPartRef,MRGRefPartSetRef -- Peek and poke the reference +/* MRGRefPartRef,MRGRefPartSetRef -- read and write the reference + * using the software barrier * * Might be more efficient to take a seg, rather than calculate it * every time. @@ -87,7 +88,7 @@ static Ref MRGRefPartRef(Arena arena, RefPart refPart) AVER(refPart != NULL); - ref = ArenaPeek(arena, &refPart->ref); + ref = ArenaRead(arena, &refPart->ref); return ref; } @@ -102,7 +103,7 @@ static void MRGRefPartSetRef(Arena arena, RefPart refPart, Ref ref) { AVER(refPart != NULL); - ArenaPoke(arena, &refPart->ref, ref); + ArenaWrite(arena, &refPart->ref, ref); } @@ -119,22 +120,26 @@ typedef struct MRGStruct { Sig sig; /* */ } MRGStruct; -#define PoolMRG(pool) PARENT(MRGStruct, poolStruct, pool) -#define MRGPool(mrg) (&(mrg)->poolStruct) +typedef MRG MRGPool; +#define MRGPoolCheck MRGCheck +DECLARE_CLASS(Pool, MRGPool, AbstractPool); /* MRGCheck -- check an MRG pool */ + ATTRIBUTE_UNUSED static Bool MRGCheck(MRG mrg) { + Pool pool = CouldBeA(AbstractPool, mrg); CHECKS(MRG, mrg); - CHECKD(Pool, MRGPool(mrg)); - CHECKL(MRGPool(mrg)->class == PoolClassMRG()); + CHECKC(MRGPool, mrg); + CHECKD(Pool, pool); + CHECKC(MRGPool, mrg); CHECKD_NOSIG(Ring, &mrg->entryRing); CHECKD_NOSIG(Ring, &mrg->freeRing); CHECKD_NOSIG(Ring, &mrg->refRing); - CHECKL(mrg->extendBy == ArenaGrainSize(PoolArena(MRGPool(mrg)))); + CHECKL(mrg->extendBy == ArenaGrainSize(PoolArena(pool))); return TRUE; } @@ -158,19 +163,12 @@ typedef struct MRGRefSegStruct { Sig sig; /* */ } MRGRefSegStruct; -/* macros to get between child and parent seg structures */ - -#define Seg2LinkSeg(seg) ((MRGLinkSeg)(seg)) -#define LinkSeg2Seg(linkseg) ((Seg)(linkseg)) - -#define Seg2RefSeg(seg) ((MRGRefSeg)(seg)) -#define RefSeg2Seg(refseg) ((Seg)(refseg)) - /* forward declarations */ -extern SegClass MRGLinkSegClassGet(void); -extern SegClass MRGRefSegClassGet(void); +DECLARE_CLASS(Seg, MRGLinkSeg, Seg); +DECLARE_CLASS(Seg, MRGRefSeg, GCSeg); +static Res mrgRefSegScan(Bool *totalReturn, Seg seg, ScanState ss); /* MRGLinkSegCheck -- check a link segment @@ -183,14 +181,13 @@ extern SegClass MRGRefSegClassGet(void); ATTRIBUTE_UNUSED static Bool MRGLinkSegCheck(MRGLinkSeg linkseg) { - Seg seg; + Seg seg = CouldBeA(Seg, linkseg); CHECKS(MRGLinkSeg, linkseg); - CHECKD(Seg, &linkseg->segStruct); - seg = LinkSeg2Seg(linkseg); + CHECKD(Seg, seg); if (NULL != linkseg->refSeg) { /* see .link.nullref */ - CHECKL(SegPool(seg) == SegPool(RefSeg2Seg(linkseg->refSeg))); CHECKU(MRGRefSeg, linkseg->refSeg); + CHECKL(SegPool(seg) == SegPool(CouldBeA(Seg, linkseg->refSeg))); CHECKL(linkseg->refSeg->linkSeg == linkseg); } return TRUE; @@ -199,12 +196,12 @@ static Bool MRGLinkSegCheck(MRGLinkSeg linkseg) ATTRIBUTE_UNUSED static Bool MRGRefSegCheck(MRGRefSeg refseg) { - Seg seg; + GCSeg gcseg = CouldBeA(GCSeg, refseg); + Seg seg = CouldBeA(Seg, gcseg); CHECKS(MRGRefSeg, refseg); - CHECKD(GCSeg, &refseg->gcSegStruct); - seg = RefSeg2Seg(refseg); - CHECKL(SegPool(seg) == SegPool(LinkSeg2Seg(refseg->linkSeg))); + CHECKD(GCSeg, gcseg); + CHECKL(SegPool(seg) == SegPool(CouldBeA(Seg, refseg->linkSeg))); CHECKD_NOSIG(Ring, &refseg->mrgRing); CHECKD(MRGLinkSeg, refseg->linkSeg); CHECKL(refseg->linkSeg->refSeg == refseg); @@ -215,46 +212,53 @@ static Bool MRGRefSegCheck(MRGRefSeg refseg) /* MRGLinkSegInit -- initialise a link segment */ static Res MRGLinkSegInit(Seg seg, Pool pool, Addr base, Size size, - Bool reservoirPermit, ArgList args) + ArgList args) { - SegClass super; MRGLinkSeg linkseg; - MRG mrg; Res res; - AVERT(Seg, seg); - linkseg = Seg2LinkSeg(seg); - AVERT(Pool, pool); - mrg = PoolMRG(pool); - AVERT(MRG, mrg); - /* no useful checks for base and size */ - AVERT(Bool, reservoirPermit); - /* Initialize the superclass fields first via next-method call */ - super = SEG_SUPERCLASS(MRGLinkSegClass); - res = super->init(seg, pool, base, size, reservoirPermit, args); + res = NextMethod(Seg, MRGLinkSeg, init)(seg, pool, base, size, args); if (res != ResOK) return res; + linkseg = CouldBeA(MRGLinkSeg, seg); + + /* no useful checks for base and size */ + linkseg->refSeg = NULL; /* .link.nullref */ + + SetClassOfPoly(seg, CLASS(MRGLinkSeg)); linkseg->sig = MRGLinkSegSig; - AVERT(MRGLinkSeg, linkseg); + AVERC(MRGLinkSeg, linkseg); return ResOK; } +/* MRGLinkSegFinish -- finish a link segment */ + +static void mrgLinkSegFinish(Inst inst) +{ + Seg seg = MustBeA(Seg, inst); + MRGLinkSeg linkseg = MustBeA(MRGLinkSeg, seg); + + linkseg->sig = SigInvalid; + + /* finish the superclass fields last */ + NextMethod(Inst, MRGLinkSeg, finish)(inst); +} + + /* MRGRefSegInit -- initialise a ref segment */ ARG_DEFINE_KEY(mrg_seg_link_seg, Pointer); #define mrgKeyLinkSeg (&_mps_key_mrg_seg_link_seg) -static Res MRGRefSegInit(Seg seg, Pool pool, Addr base, Size size, - Bool reservoirPermit, ArgList args) +static Res MRGRefSegInit(Seg seg, Pool pool, Addr base, Size size, ArgList args) { MRGLinkSeg linkseg; MRGRefSeg refseg; - MRG mrg; - SegClass super; + MRG mrg = MustBeA(MRGPool, pool); Res res; ArgStruct arg; @@ -265,20 +269,14 @@ static Res MRGRefSegInit(Seg seg, Pool pool, Addr base, Size size, ArgRequire(&arg, args, mrgKeyLinkSeg); linkseg = arg.val.p; - AVERT(Seg, seg); - refseg = Seg2RefSeg(seg); - AVERT(Pool, pool); - mrg = PoolMRG(pool); - AVERT(MRG, mrg); - /* no useful checks for base and size */ - AVERT(Bool, reservoirPermit); - AVERT(MRGLinkSeg, linkseg); - /* Initialize the superclass fields first via next-method call */ - super = SEG_SUPERCLASS(MRGRefSegClass); - res = super->init(seg, pool, base, size, reservoirPermit, args); + res = NextMethod(Seg, MRGRefSeg, init)(seg, pool, base, size, args); if (res != ResOK) return res; + refseg = CouldBeA(MRGRefSeg, seg); + + /* no useful checks for base and size */ + AVERT(MRGLinkSeg, linkseg); /* , .improve.rank */ SegSetRankSet(seg, RankSetSingle(RankFINAL)); @@ -287,39 +285,56 @@ static Res MRGRefSegInit(Seg seg, Pool pool, Addr base, Size size, RingAppend(&mrg->refRing, &refseg->mrgRing); refseg->linkSeg = linkseg; AVER(NULL == linkseg->refSeg); /* .link.nullref */ + + SetClassOfPoly(seg, CLASS(MRGRefSeg)); refseg->sig = MRGRefSegSig; linkseg->refSeg = refseg; /* .ref.initarg */ - AVERT(MRGRefSeg, refseg); + AVERC(MRGRefSeg, refseg); AVERT(MRGLinkSeg, linkseg); return ResOK; } +/* MRGRefSegFinish -- finish a ref segment */ + +static void mrgRefSegFinish(Inst inst) +{ + Seg seg = MustBeA(Seg, inst); + MRGRefSeg refseg = MustBeA(MRGRefSeg, seg); + + refseg->sig = SigInvalid; + + /* finish the superclass fields last */ + NextMethod(Inst, MRGRefSeg, finish)(inst); +} + + /* MRGLinkSegClass -- Class definition */ -DEFINE_SEG_CLASS(MRGLinkSegClass, class) +DEFINE_CLASS(Seg, MRGLinkSeg, klass) { - INHERIT_CLASS(class, SegClass); - SegClassMixInNoSplitMerge(class); /* no support for this */ - class->name = "MRGLSEG"; - class->size = sizeof(MRGLinkSegStruct); - class->init = MRGLinkSegInit; - AVERT(SegClass, class); + INHERIT_CLASS(klass, MRGLinkSeg, Seg); + SegClassMixInNoSplitMerge(klass); /* no support for this */ + klass->instClassStruct.finish = mrgLinkSegFinish; + klass->size = sizeof(MRGLinkSegStruct); + klass->init = MRGLinkSegInit; + AVERT(SegClass, klass); } /* MRGRefSegClass -- Class definition */ -DEFINE_SEG_CLASS(MRGRefSegClass, class) +DEFINE_CLASS(Seg, MRGRefSeg, klass) { - INHERIT_CLASS(class, GCSegClass); - SegClassMixInNoSplitMerge(class); /* no support for this */ - class->name = "MRGRSEG"; - class->size = sizeof(MRGRefSegStruct); - class->init = MRGRefSegInit; - AVERT(SegClass, class); + INHERIT_CLASS(klass, MRGRefSeg, GCSeg); + SegClassMixInNoSplitMerge(klass); /* no support for this */ + klass->instClassStruct.finish = mrgRefSegFinish; + klass->size = sizeof(MRGRefSegStruct); + klass->init = MRGRefSegInit; + klass->scan = mrgRefSegScan; + AVERT(SegClass, klass); } @@ -339,7 +354,7 @@ static Count MRGGuardiansPerSeg(MRG mrg) #define refPartOfIndex(refseg, index) \ - ((RefPart)SegBase(RefSeg2Seg(refseg)) + (index)) + ((RefPart)SegBase(MustBeA(Seg, refseg)) + (index)) static RefPart MRGRefPartOfLink(Link link, Arena arena) @@ -354,20 +369,19 @@ static RefPart MRGRefPartOfLink(Link link, Arena arena) b = SegOfAddr(&seg, arena, (Addr)link); AVER(b); - AVER(SegPool(seg)->class == PoolClassMRG()); - linkseg = Seg2LinkSeg(seg); - AVERT(MRGLinkSeg, linkseg); + AVERC(MRGPool, SegPool(seg)); + linkseg = MustBeA(MRGLinkSeg, seg); linkBase = (Link)SegBase(seg); AVER(link >= linkBase); indx = (Index)(link - linkBase); - AVER(indx < MRGGuardiansPerSeg(PoolMRG(SegPool(seg)))); + AVER(indx < MRGGuardiansPerSeg(MustBeA(MRGPool, SegPool(seg)))); return refPartOfIndex(linkseg->refSeg, indx); } #define linkOfIndex(linkseg, index) \ - ((Link)SegBase(LinkSeg2Seg(linkseg)) + (index)) + ((Link)SegBase(MustBeA(Seg, linkseg)) + (index)) #if 0 @@ -383,7 +397,7 @@ static Link MRGLinkOfRefPart(RefPart refPart, Arena arena) b = SegOfAddr(&seg, arena, (Addr)refPart); AVER(b); - AVER(SegPool(seg)->class == PoolClassMRG()); + AVER(SegPool(seg)->klass == PoolClassMRG()); refseg = Seg2RefSeg(seg); AVERT(MRGRefSeg, refseg); refPartBase = (RefPart)SegBase(seg); @@ -408,7 +422,7 @@ static void MRGGuardianInit(MRG mrg, Link link, RefPart refPart) link->state = MRGGuardianFREE; RingAppend(&mrg->freeRing, &link->the.linkRing); /* */ - MRGRefPartSetRef(PoolArena(MRGPool(mrg)), refPart, 0); + MRGRefPartSetRef(PoolArena(MustBeA(AbstractPool, mrg)), refPart, 0); } @@ -429,12 +443,12 @@ static void MRGMessageDelete(Message message) arena = MessageArena(message); b = PoolOfAddr(&pool, arena, (Addr)message); AVER(b); - AVER(pool->class == PoolClassMRG()); + AVERC(MRGPool, pool); link = linkOfMessage(message); AVER(link->state == MRGGuardianFINAL); MessageFinish(message); - MRGGuardianInit(PoolMRG(pool), link, MRGRefPartOfLink(link, arena)); + MRGGuardianInit(MustBeA(MRGPool, pool), link, MRGRefPartOfLink(link, arena)); } @@ -491,54 +505,49 @@ static void MRGSegPairDestroy(MRGRefSeg refseg) { RingRemove(&refseg->mrgRing); RingFinish(&refseg->mrgRing); - refseg->sig = SigInvalid; - SegFree(LinkSeg2Seg(refseg->linkSeg)); - SegFree(RefSeg2Seg(refseg)); + SegFree(MustBeA(Seg, refseg->linkSeg)); + SegFree(MustBeA(Seg, refseg)); } /* MRGSegPairCreate -- create a pair of segments (link & ref) */ -static Res MRGSegPairCreate(MRGRefSeg *refSegReturn, MRG mrg, - Bool withReservoirPermit) +static Res MRGSegPairCreate(MRGRefSeg *refSegReturn, MRG mrg) { + Pool pool = MustBeA(AbstractPool, mrg); + Arena arena = PoolArena(pool); RefPart refPartBase; Count nGuardians; /* guardians per seg */ Index i; Link linkBase; - Pool pool; Res res; Seg segLink, segRefPart; MRGLinkSeg linkseg; MRGRefSeg refseg; Size linkSegSize; - Arena arena; AVER(refSegReturn != NULL); - pool = MRGPool(mrg); - arena = PoolArena(pool); - nGuardians = MRGGuardiansPerSeg(mrg); linkSegSize = nGuardians * sizeof(LinkStruct); linkSegSize = SizeArenaGrains(linkSegSize, arena); - res = SegAlloc(&segLink, EnsureMRGLinkSegClass(), + res = SegAlloc(&segLink, CLASS(MRGLinkSeg), LocusPrefDefault(), linkSegSize, pool, - withReservoirPermit, argsNone); + argsNone); if (res != ResOK) goto failLinkSegAlloc; - linkseg = Seg2LinkSeg(segLink); + linkseg = MustBeA(MRGLinkSeg, segLink); MPS_ARGS_BEGIN(args) { MPS_ARGS_ADD_FIELD(args, mrgKeyLinkSeg, p, linkseg); /* .ref.initarg */ - res = SegAlloc(&segRefPart, EnsureMRGRefSegClass(), + res = SegAlloc(&segRefPart, CLASS(MRGRefSeg), LocusPrefDefault(), mrg->extendBy, pool, - withReservoirPermit, args); + args); } MPS_ARGS_END(args); if (res != ResOK) goto failRefPartSegAlloc; - refseg = Seg2RefSeg(segRefPart); + refseg = MustBeA(MRGRefSeg, segRefPart); linkBase = (Link)SegBase(segLink); refPartBase = (RefPart)SegBase(segRefPart); @@ -566,7 +575,7 @@ static void MRGFinalize(Arena arena, MRGLinkSeg linkseg, Index indx) Link link; Message message; - AVER(indx < MRGGuardiansPerSeg(PoolMRG(SegPool(LinkSeg2Seg(linkseg))))); + AVER(indx < MRGGuardiansPerSeg(MustBeA(MRGPool, SegPool(MustBeA(Seg, linkseg))))); link = linkOfIndex(linkseg, indx); @@ -583,21 +592,22 @@ static void MRGFinalize(Arena arena, MRGLinkSeg linkseg, Index indx) } -static Res MRGRefSegScan(ScanState ss, MRGRefSeg refseg, MRG mrg) +static Res mrgRefSegScan(Bool *totalReturn, Seg seg, ScanState ss) { + MRGRefSeg refseg = MustBeA(MRGRefSeg, seg); + Pool pool = SegPool(seg); + MRG mrg = MustBeA(MRGPool, pool); + Res res; Arena arena; MRGLinkSeg linkseg; - RefPart refPart; Index i; Count nGuardians; AVERT(ScanState, ss); - AVERT(MRGRefSeg, refseg); - AVERT(MRG, mrg); - arena = PoolArena(MRGPool(mrg)); + arena = PoolArena(pool); linkseg = refseg->linkSeg; nGuardians = MRGGuardiansPerSeg(mrg); @@ -613,56 +623,67 @@ static Res MRGRefSegScan(ScanState ss, MRGRefSeg refseg, MRG mrg) /* because we are in a scan and the shield is exposed. */ if (TRACE_FIX1(ss, refPart->ref)) { res = TRACE_FIX2(ss, &(refPart->ref)); - if (res != ResOK) + if (res != ResOK) { + *totalReturn = FALSE; return res; + } if (ss->rank == RankFINAL && !ss->wasMarked) { /* .improve.rank */ MRGFinalize(arena, linkseg, i); } } + ss->scannedSize += sizeof *refPart; } } } TRACE_SCAN_END(ss); + *totalReturn = TRUE; return ResOK; } /* MRGInit -- init method for MRG */ -static Res MRGInit(Pool pool, ArgList args) +static Res MRGInit(Pool pool, Arena arena, PoolClass klass, ArgList args) { MRG mrg; + Res res; - AVER(pool != NULL); /* Can't check more; see pool contract @@@@ */ + AVER(pool != NULL); AVERT(ArgList, args); UNUSED(args); - - mrg = PoolMRG(pool); + UNUSED(klass); /* used for debug pools only */ + res = NextMethod(Pool, MRGPool, init)(pool, arena, klass, args); + if (res != ResOK) + goto failNextInit; + mrg = CouldBeA(MRGPool, pool); + RingInit(&mrg->entryRing); RingInit(&mrg->freeRing); RingInit(&mrg->refRing); mrg->extendBy = ArenaGrainSize(PoolArena(pool)); - mrg->sig = MRGSig; - AVERT(MRG, mrg); - EVENT3(PoolInit, pool, PoolArena(pool), ClassOfPool(pool)); + SetClassOfPoly(pool, CLASS(MRGPool)); + mrg->sig = MRGSig; + AVERC(MRGPool, mrg); + return ResOK; + +failNextInit: + AVER(res != ResOK); + return res; } /* MRGFinish -- finish a MRG pool */ -static void MRGFinish(Pool pool) +static void MRGFinish(Inst inst) { - MRG mrg; + Pool pool = MustBeA(AbstractPool, inst); + MRG mrg = MustBeA(MRGPool, pool); Ring node, nextNode; - AVERT(Pool, pool); - mrg = PoolMRG(pool); - AVERT(MRG, mrg); - /* .finish.ring: Before destroying the segments, we isolate the */ /* rings in the pool structure. The problem we are avoiding here */ /* is when the rings point to memory that has been unmapped by one */ @@ -696,6 +717,8 @@ static void MRGFinish(Pool pool) mrg->sig = SigInvalid; RingFinish(&mrg->refRing); /* */ + + NextMethod(Inst, MRGPool, finish)(inst); } @@ -703,27 +726,19 @@ static void MRGFinish(Pool pool) Res MRGRegister(Pool pool, Ref ref) { + MRG mrg = MustBeA(MRGPool, pool); + Arena arena = PoolArena(pool); Ring freeNode; - Arena arena; Link link; RefPart refPart; - MRG mrg; Res res; MRGRefSeg junk; /* unused */ - AVERT(Pool, pool); AVER(ref != 0); - mrg = PoolMRG(pool); - AVERT(MRG, mrg); - - arena = PoolArena(pool); - AVERT(Arena, arena); - /* */ if (RingIsSingle(&mrg->freeRing)) { - /* @@@@ Should the client be able to use the reservoir for this? */ - res = MRGSegPairCreate(&junk, mrg, /* withReservoirPermit */ FALSE); + res = MRGSegPairCreate(&junk, mrg); if (res != ResOK) return res; } @@ -754,18 +769,14 @@ Res MRGRegister(Pool pool, Ref ref) Res MRGDeregister(Pool pool, Ref obj) { + MRG mrg = MustBeA(MRGPool, pool); + Arena arena = PoolArena(pool); Ring node, nextNode; Count nGuardians; /* guardians per seg */ - Arena arena; - MRG mrg; - AVERT(Pool, pool); /* Can't check obj */ - mrg = PoolMRG(pool); - AVERT(MRG, mrg); nGuardians = MRGGuardiansPerSeg(mrg); - arena = PoolArena(pool); /* map over the segments */ RING_FOR(node, &mrg->refRing, nextNode) { @@ -778,8 +789,8 @@ Res MRGDeregister(Pool pool, Ref obj) AVERT(MRGRefSeg, refSeg); linkSeg = refSeg->linkSeg; /* map over each guardian in the segment */ - for(i = 0, link = (Link)SegBase(LinkSeg2Seg(linkSeg)), - refPart = (RefPart)SegBase(RefSeg2Seg(refSeg)); + for(i = 0, link = (Link)SegBase(MustBeA(Seg, linkSeg)), + refPart = (RefPart)SegBase(MustBeA(Seg, refSeg)); i < nGuardians; ++i, ++link, ++refPart) { /* check if it's allocated and points to obj */ @@ -801,36 +812,40 @@ 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, Count depth) + +static Res MRGDescribe(Inst inst, mps_lib_FILE *stream, Count depth) { - MRG mrg; + Pool pool = CouldBeA(AbstractPool, inst); + MRG mrg = CouldBeA(MRGPool, pool); Arena arena; Ring node, nextNode; RefPart refPart; Res res; - if (!TESTT(Pool, pool)) - return ResFAIL; - mrg = PoolMRG(pool); - if (!TESTT(MRG, mrg)) - return ResFAIL; + if (!TESTC(MRGPool, mrg)) + return ResPARAM; if (stream == NULL) - return ResFAIL; + return ResPARAM; + res = NextMethod(Inst, MRGPool, describe)(inst, stream, depth); + if (res != ResOK) + return res; + + res = WriteF(stream, depth + 2, "extendBy $W\n", (WriteFW)mrg->extendBy, NULL); + if (res != ResOK) + return res; + + res = WriteF(stream, depth + 2, "Entry queue:\n", NULL); + if (res != ResOK) + return res; arena = PoolArena(pool); - 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; + Bool outsideShield = !ArenaShield(arena)->inside; refPart = MRGRefPartOfLink(linkOfRing(node), arena); if (outsideShield) { ShieldEnter(arena); } - res = WriteF(stream, depth, "at $A Ref $A\n", + res = WriteF(stream, depth + 2, "at $A Ref $A\n", (WriteFA)refPart, (WriteFA)MRGRefPartRef(arena, refPart), NULL); if (outsideShield) { @@ -844,60 +859,26 @@ static Res MRGDescribe(Pool pool, mps_lib_FILE *stream, Count depth) } -static Res MRGScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg) +DEFINE_CLASS(Pool, MRGPool, klass) { - MRG mrg; - Res res; - MRGRefSeg refseg; - - AVERT(ScanState, ss); - AVERT(Pool, pool); - AVERT(Seg, seg); - - mrg = PoolMRG(pool); - AVERT(MRG, mrg); - - AVER(SegRankSet(seg) == RankSetSingle(RankFINAL)); /* .improve.rank */ - AVER(TraceSetInter(SegGrey(seg), ss->traces) != TraceSetEMPTY); - refseg = Seg2RefSeg(seg); - AVERT(MRGRefSeg, refseg); - - res = MRGRefSegScan(ss, refseg, mrg); - if (res != ResOK) { - *totalReturn = FALSE; - return res; - } - - *totalReturn = TRUE; - return ResOK; -} - - -DEFINE_POOL_CLASS(MRGPoolClass, this) -{ - INHERIT_CLASS(this, AbstractPoolClass); - this->name = "MRG"; - this->size = sizeof(MRGStruct); - this->offset = offsetof(MRGStruct, poolStruct); - this->init = MRGInit; - this->finish = MRGFinish; - this->grey = PoolTrivGrey; - this->blacken = PoolTrivBlacken; - this->scan = MRGScan; - this->describe = MRGDescribe; - AVERT(PoolClass, this); + INHERIT_CLASS(klass, MRGPool, AbstractPool); + klass->instClassStruct.describe = MRGDescribe; + klass->instClassStruct.finish = MRGFinish; + klass->size = sizeof(MRGStruct); + klass->init = MRGInit; + AVERT(PoolClass, klass); } PoolClass PoolClassMRG(void) { - return MRGPoolClassGet(); + return CLASS(MRGPool); } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/poolmv.c b/mps/code/poolmv.c deleted file mode 100644 index 19b3c2bb443..00000000000 --- a/mps/code/poolmv.c +++ /dev/null @@ -1,958 +0,0 @@ -/* poolmv.c: MANUAL VARIABLE POOL - * - * $Id$ - * 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 - * pool, since it is used to implement that pool. - * - * An observation: Freeing memory introduces more information - * into the system than allocating it. This causes the problem - * described in note 2. - * - * Notes - * 1. Need to measure typical fragmentation levels and adjust the - * blockExtendBy parameter appropriately. richard 1994-11-08 - * 2. free can lose memory if it can't allocate a block descriptor. The - * memory could be pushed onto a special chain to be reclaimed later. - * richard 1994-11-09 - * 3. The span chain could be adaptive. richard 1994-11-09 - * 5. An MFS pool for the block descriptors is justified, but not really - * for the spans, which are much rarer. richard 1994-11-09 - * 8. By changing MVSpanAlloc it might be possible to keep track of all - * allocated blocks using descriptors, for debugging purposes. richard - * 1994-11-10 - */ - -#include "mpscmv.h" -#include "dbgpool.h" -#include "poolmv.h" -#include "poolmfs.h" -#include "mpm.h" - -SRCID(poolmv, "$Id$"); - - -#define mvBlockPool(mv) MFSPool(&(mv)->blockPoolStruct) -#define mvSpanPool(mv) MFSPool(&(mv)->spanPoolStruct) - - -#define PoolMV(pool) PARENT(MVStruct, poolStruct, pool) - - -/* MVDebug -- MV Debug pool class */ - -typedef struct MVDebugStruct { - MVStruct MVStruct; /* MV structure */ - PoolDebugMixinStruct debug; /* debug mixin */ -} MVDebugStruct; - -typedef MVDebugStruct *MVDebug; - - -#define MV2MVDebug(mv) PARENT(MVDebugStruct, MVStruct, mv) -#define MVDebug2MV(mvd) (&((mvd)->MVStruct)) - - -/* MVBlockStruct -- block structure - * - * The pool maintains a descriptor structure for each contiguous - * allocated block of memory it manages. The descriptor is on a simple - * linked-list of such descriptors, which is in ascending order of - * address. - */ - -typedef struct MVBlockStruct *MVBlock; -typedef struct MVBlockStruct { - MVBlock next; - Addr base, limit; -} MVBlockStruct; - - -/* MVBlockCheck -- check the consistency of a block structure */ - -ATTRIBUTE_UNUSED -static Bool MVBlockCheck(MVBlock block) -{ - AVER(block != NULL); - AVER(block->limit >= block->base); - /* Check that it is in the block pool. See note 7. */ - /* This turns out to be considerably tricky, as we cannot get hold */ - /* of the blockPool (pool is not a parameter). */ - return TRUE; -} - - -/* MVSpanStruct -- span structure - * - * The pool maintains a wrapper for each span allocated from the arena - * which contains a chain of descriptors for the allocated memory in that - * span. It also contains sentinel block descriptors which mark the - * start and end of the span. These blocks considerably simplify - * allocation, and may be zero-sized. - * - * .design.largest: If 'largestKnown' is TRUE, 'largest' is the size - * of the largest free block in the span. Otherwise, 'largest' is - * one more than the span size. - * - * .design.largest.alloc: When seeking a span in which to allocate, - * a span should not be examined if 'largest' is less than the - * space sought. - * - * .design.largest.free: When freeing, compute the size of the new - * free area. If it is larger than 'largest', set 'largest' to it. - */ - -#define MVSpanSig ((Sig)0x5193F5BA) /* SIGnature MV SPAn */ - -typedef struct MVSpanStruct *MVSpan; -typedef struct MVSpanStruct { - Sig sig; /* */ - RingStruct spans; /* all the spans */ - MV mv; /* owning MV pool */ - Tract tract; /* first tract of the span */ - Size size; /* size of the span */ - MVBlockStruct base; /* sentinel at base of span */ - MVBlockStruct limit; /* sentinel at limit of span */ - MVBlock blocks; /* allocated blocks */ - Size free; /* free space in span */ - Size largest; /* .design.largest */ - Bool largestKnown; /* .design.largest */ - unsigned blockCount; /* number of blocks on chain */ -} MVSpanStruct; - - -#define SpanSize(span) \ - AddrOffset((span)->base.base, (span)->limit.limit) -#define SpanInsideSentinels(span) \ - AddrOffset((span)->base.limit, (span)->limit.base) - - -/* MVSpanCheck -- check the consistency of a span structure */ - -ATTRIBUTE_UNUSED -static Bool MVSpanCheck(MVSpan span) -{ - Addr base, limit; - - CHECKS(MVSpan, span); - - CHECKD_NOSIG(Ring, &span->spans); - CHECKU(MV, span->mv); - CHECKD_NOSIG(Tract, span->tract); - 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 */ - /* base sentinel... */ - CHECKL(span->base.next != NULL); - /* ... and it's sure to have at least two blocks on it. */ - CHECKL(span->blockCount >= 2); - /* This is just defined this way. It shouldn't change. */ - CHECKL(span->limit.next == NULL); - /* The sentinels should mark the ends of the span. */ - base = TractBase(span->tract); - limit = AddrAdd(base, span->size); - CHECKL(span->base.base == base); - CHECKL(span->limit.limit == limit); - /* 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->free <= SpanInsideSentinels(span)); - - CHECKL(BoolCheck(span->largestKnown)); - if (span->largestKnown) { /* .design.largest */ - CHECKL(span->largest <= span->free); - /* at least this much is free */ - } else { - CHECKL(span->largest == SpanSize(span)+1); - } - - /* 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); - } -#endif - - return TRUE; -} - - -/* MVVarargs -- decode obsolete varargs */ - -static void MVVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) -{ - args[0].key = MPS_KEY_EXTEND_BY; - args[0].val.size = va_arg(varargs, Size); - args[1].key = MPS_KEY_MEAN_SIZE; - args[1].val.size = va_arg(varargs, Size); - args[2].key = MPS_KEY_MAX_SIZE; - args[2].val.size = va_arg(varargs, Size); - args[3].key = MPS_KEY_ARGS_END; - AVERT(ArgList, args); -} - -static void MVDebugVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) -{ - args[0].key = MPS_KEY_POOL_DEBUG_OPTIONS; - args[0].val.pool_debug_options = va_arg(varargs, mps_pool_debug_option_s *); - MVVarargs(args + 1, varargs); -} - - -/* MVInit -- init method for class MV */ - -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; - Size blockExtendBy, spanExtendBy; - MV mv; - Arena arena; - 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)) - avgSize = arg.val.size; - 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); - - pool->alignment = align; - mv = PoolMV(pool); - arena = PoolArena(pool); - - /* At 100% fragmentation we will need one block descriptor for every other */ - /* allocated block, or (extendBy/avgSize)/2 descriptors. See note 1. */ - blockExtendBy = sizeof(MVBlockStruct) * (extendBy/avgSize)/2; - if(blockExtendBy < sizeof(MVBlockStruct)) { - blockExtendBy = sizeof(MVBlockStruct); - } - - 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(mvBlockPool(mv), arena, PoolClassMFS(), piArgs); - } MPS_ARGS_END(piArgs); - if(res != ResOK) - 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(mvSpanPool(mv), arena, PoolClassMFS(), piArgs); - } MPS_ARGS_END(piArgs); - if(res != ResOK) - goto failSpanPoolInit; - - mv->extendBy = extendBy; - mv->avgSize = avgSize; - mv->maxSize = maxSize; - RingInit(&mv->spans); - - 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; -} - - -/* MVFinish -- finish method for class MV */ - -static void MVFinish(Pool pool) -{ - MV mv; - Ring spans, node = NULL, nextNode; /* gcc whinge stop */ - MVSpan span; - - AVERT(Pool, pool); - mv = PoolMV(pool); - AVERT(MV, mv); - - /* Destroy all the spans attached to the pool. */ - spans = &mv->spans; - RING_FOR(node, spans, nextNode) { - span = RING_ELT(MVSpan, spans, node); - AVERT(MVSpan, span); - ArenaFree(TractBase(span->tract), span->size, pool); - } - - mv->sig = SigInvalid; - - PoolFinish(mvBlockPool(mv)); - PoolFinish(mvSpanPool(mv)); -} - - -/* MVSpanAlloc -- allocate space from a span of memory - * - * MVSpanAlloc searches a span for a free block of the requested size. If it - * finds one it allocates it from the span, updates *addrReturn to point - * to it, and returns TRUE. - */ - -static Bool MVSpanAlloc(Addr *addrReturn, MVSpan span, Size size, - Pool blockPool) -{ - Size gap; - Size largest = 0; - MVBlock block; - - AVERT(MVSpan, span); - AVER(size > 0); - AVER(addrReturn != NULL); - - block = span->blocks; - AVER(block == &span->base); /* should be the base sentinel */ - - /* We're guaranteed at least one gap between sentinels, and therefore at */ - /* least one iteration of this loop. So, the test is at the end. */ - do { - AVER(block->next != NULL); - - gap = AddrOffset(block->limit, block->next->base); - - if (gap > largest) { - largest = gap; - AVER(largest <= span->largest); - } - - if(gap >= size) { - Addr new = block->limit; - - /* If the gap is exactly the right size then the preceeding and */ - /* following blocks can be merged, into the preceeding one, */ - /* unless the following block is the end sentinel. */ - if(gap == size && block->next != &span->limit) { - MVBlock old = block->next; - block->limit = old->limit; - block->next = old->next; - PoolFree(blockPool, (Addr)old, sizeof(MVBlockStruct)); - --span->blockCount; - } else - block->limit = AddrAdd(block->limit, size); - - if (gap == span->largest) { /* we've used a 'largest' gap */ - AVER(span->largestKnown); - span->largestKnown = FALSE; - span->largest = SpanSize(span) + 1; /* .design.largest */ - } - - span->free -= size; - *addrReturn = new; - return TRUE; - } - - block = block->next; - } - while(block->next != NULL); - - /* we've looked at all the gaps, so now we know the largest */ - AVER(span->largestKnown == FALSE); - span->largestKnown = TRUE; - span->largest = largest; - - return FALSE; -} - - -/* MVSpanFree -- free an area in a span of memory - * - * Searches a span for a block which contains the area specified by the - * base and limit, and frees it within that span. This may involve - * allocating a block descriptor, which may fail, in which case an error is - * returned. - * - * There are eight cases, depending on what we are freeing: - * 1. whole of non-sentinel - * 2. in body of any block - * 3. at base of non-base - * 4. at limit of non-limit - * 5. whole of base sentinel - * 6. whole of limit sentinel - * 7. at base of base sentinel - * 8. at limit of limit sentinel - */ - -static Res MVSpanFree(MVSpan span, Addr base, Addr limit, Pool blockPool) -{ - MVBlock prev, block; - Size freeAreaSize = 0; /* .design.largest.free */ - - AVERT(MVSpan, span); - AVER(span->base.base <= base); - AVER(limit <= span->limit.limit); - AVERT(Pool, blockPool); - - prev = NULL; - block = span->blocks; - - AVER(block == &span->base); /* should be base sentinel */ - do { - AVERT(MVBlock, block); - - /* Is the freed area within the block? */ - if(block->base <= base && limit <= block->limit) { - Bool isBase = block == &span->base; - Bool isLimit = block == &span->limit; - Bool isSentinel = isBase || isLimit; - - if(!isSentinel && block->base == base && limit == block->limit) { - /* case 1 : the whole of a non-sentinel block */ - AVER(block->next != NULL); /* there must at least be a sentinel */ - AVER(prev != NULL); /* block isn't sentinel */ - freeAreaSize = AddrOffset(prev->limit, block->next->base); - prev->next = block->next; - PoolFree(blockPool, (Addr)block, sizeof(MVBlockStruct)); - --span->blockCount; - } else if(!isBase && block->base == base) { - /* cases 3 and 6: at base of a block other than the base sentinel */ - AVER(prev != NULL); /* block isn't sentinel */ - freeAreaSize = AddrOffset(prev->limit, limit); - block->base = limit; - } else if(!isLimit && limit == block->limit) { - /* cases 4 and 5: at limit of a block other than the limit sentinel */ - AVER(block->next != NULL); /* should at least be a sentinel */ - freeAreaSize = AddrOffset(base, block->next->base); - block->limit = base; - } else { - /* cases 2, 7, and 8: making a new fragment */ - Res res; - MVBlock new; - Addr addr; - - /* The freed area is buried in the middle of the block, so the */ - /* block must be split into two parts. */ - res = PoolAlloc(&addr, blockPool, sizeof(MVBlockStruct), - /* withReservoirPermit */ FALSE); - if (res != ResOK) - return res; - new = (MVBlock)addr; - - freeAreaSize = AddrOffset(base, limit); - - /* If the freed area is in the base sentinel then insert the new */ - /* descriptor after it, otherwise insert before. */ - if(isBase) { /* case 7: new fragment at the base of the span */ - new->base = limit; - new->limit = block->limit; - block->limit = base; - new->next = block->next; - AVER(new->next != NULL); /* should at least be a sentinel */ - block->next = new; - } else { /* cases 2 and 8 */ - new->base = block->base; - new->limit = base; - block->base = limit; - new->next = block; - AVER(prev != NULL); - prev->next = new; - } - - AVERT(MVBlock, new); - ++span->blockCount; - } - - AVERT(MVBlock, block); - - span->free += AddrOffset(base, limit); - - if (freeAreaSize > span->largest) { /* .design.largest */ - AVER(span->largestKnown); - span->largest = freeAreaSize; - } - - return ResOK; - } - - prev = block; - block = block->next; - } while(block != NULL); - - /* The freed area is in the span, but not within a block. */ - NOTREACHED; - - return ResOK; -} - - -/* MVAlloc -- allocate method for class MV */ - -static Res MVAlloc(Addr *pReturn, Pool pool, Size size, - Bool withReservoirPermit) -{ - Res res; - MVSpan span; - Arena arena; - Addr base, limit, addr; - Tract tract; - MV mv; - Size regionSize; - Ring spans, node = NULL, nextNode; /* gcc whinge stop */ - - AVER(pReturn != NULL); - AVERT(Pool, pool); - mv = PoolMV(pool); - AVERT(MV, mv); - AVER(size > 0); - AVERT(Bool, withReservoirPermit); - - size = SizeAlignUp(size, pool->alignment); - - 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->free)) { - Addr new; - - if(MVSpanAlloc(&new, span, size, mvBlockPool(mv))) { - mv->free -= size; - AVER(AddrIsAligned(new, pool->alignment)); - *pReturn = new; - return ResOK; - } - } - } - } - - /* There is no block large enough in any of the spans, so extend the */ - /* pool with a new region which will hold the requested allocation. */ - /* Allocate a new span descriptor and initialize it to point at the */ - /* region. */ - res = PoolAlloc(&addr, mvSpanPool(mv), sizeof(MVSpanStruct), - withReservoirPermit); - if(res != ResOK) - return res; - span = (MVSpan)addr; - - if(size <= mv->extendBy) - regionSize = mv->extendBy; - else - regionSize = size; - - arena = PoolArena(pool); - regionSize = SizeArenaGrains(regionSize, arena); - - res = ArenaAlloc(&base, LocusPrefDefault(), regionSize, pool, - withReservoirPermit); - if(res != ResOK) { /* try again with a region big enough for this object */ - regionSize = SizeArenaGrains(size, arena); - res = ArenaAlloc(&base, LocusPrefDefault(), regionSize, pool, - withReservoirPermit); - if (res != ResOK) { - PoolFree(mvSpanPool(mv), (Addr)span, sizeof(MVSpanStruct)); - return res; - } - } - limit = AddrAdd(base, regionSize); - - DebugPoolFreeSplat(pool, base, limit); - - span->size = regionSize; - span->tract = TractOfBaseAddr(arena, base); - span->mv = mv; - /* Set the p field for each tract of the span */ - TRACT_FOR(tract, addr, arena, base, limit) { - AVERT(Tract, tract); - AVER(TractP(tract) == NULL); - AVER(TractPool(tract) == pool); - TractSetP(tract, (void *)span); - } - AVER(addr == limit); - RingInit(&span->spans); - span->base.base = span->base.limit = base; - span->limit.base = span->limit.limit = limit; - 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->free -= size; - span->largest = span->free; - span->largestKnown = TRUE; - - span->sig = MVSpanSig; - AVERT(MVSpan, span); - - mv->free += span->free; - RingInsert(&mv->spans, &span->spans); - /* use RingInsert so that we examine this new span first when allocating */ - - *pReturn = span->base.base; - return ResOK; -} - - -/* MVFree -- free method for class MV */ - -static void MVFree(Pool pool, Addr old, Size size) -{ - Addr base, limit; - MVSpan span; - MV mv; - Res res; - Bool b; - Tract tract = NULL; /* suppress "may be used uninitialized" */ - - AVERT(Pool, pool); - mv = PoolMV(pool); - AVERT(MV, mv); - - AVER(old != (Addr)0); - AVER(AddrIsAligned(old, pool->alignment)); - AVER(size > 0); - - size = SizeAlignUp(size, pool->alignment); - base = old; - limit = AddrAdd(base, size); - - /* Map the pointer onto the tract which contains it, and thence */ - /* onto the span. */ - b = TractOfAddr(&tract, PoolArena(pool), old); - AVER(b); - span = (MVSpan)TractP(tract); - AVERT(MVSpan, span); - - /* the to be freed area should be within the span just found */ - AVER(span->base.base <= base); - AVER(limit <= span->limit.limit); - - /* Unfortunately, if allocating the new block descriptor fails we */ - /* can't do anything, and the memory is lost. See note 2. */ - res = MVSpanFree(span, base, limit, mvBlockPool(mv)); - if(res != ResOK) - mv->lost += size; - else - mv->free += size; - - /* free space should be less than total space */ - 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->free -= span->free; - ArenaFree(TractBase(span->tract), span->size, pool); - RingRemove(&span->spans); - RingFinish(&span->spans); - PoolFree(mvSpanPool(mv), (Addr)span, sizeof(MVSpanStruct)); - } -} - - -/* MVDebugMixin - find debug mixin in class MVDebug */ - -static PoolDebugMixin MVDebugMixin(Pool pool) -{ - MV mv; - - AVERT(Pool, pool); - mv = PoolMV(pool); - AVERT(MV, mv); - /* Can't check MVDebug, because this is called during MVDebug init */ - return &(MV2MVDebug(mv)->debug); -} - - -/* 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; - MVSpan span; - Align step; - Size length; - char c; - Ring spans, node = NULL, nextNode; /* gcc whinge stop */ - - if (!TESTT(Pool, pool)) - return ResFAIL; - mv = PoolMV(pool); - if (!TESTT(MV, mv)) - return ResFAIL; - if (stream == NULL) - return ResFAIL; - - res = WriteF(stream, depth, - "blockPool $P ($U)\n", - (WriteFP)mvBlockPool(mv), (WriteFU)mvBlockPool(mv)->serial, - "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, - "free $W\n", (WriteFP)mv->free, - "lost $W\n", (WriteFP)mv->lost, - 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, depth, "MVSpan $P {\n", (WriteFP)span, NULL); - if (res != ResOK) - return res; - - 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 (span->largestKnown) /* .design.largest */ - res = WriteF(stream, 0, "$W\n", (WriteFW)span->largest, NULL); - else - 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, depth + 2, "$A ", (WriteFA)i, NULL); - if (res != ResOK) - return res; - - for(j = i; - j < AddrAdd(i, length) && j < span->limit.limit; - j = AddrAdd(j, step)) { - - if(j >= block->limit) { - block = block->next; - if(block == NULL) return ResFAIL; /* shouldn't pass limit */ - } - - if(j == block->base) { - if(AddrAdd(j, step) == block->limit) - c = 'O'; - else - c = '['; - } else if(j < block->base) - c = '.'; - else if(AddrAdd(j, step) == block->limit) - c = ']'; - else /* j > block->base && j < block->limit */ - c = '='; - 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, "} MVSpan $P\n", (WriteFP)span, NULL); - if (res != ResOK) - return res; - } - - return ResOK; -} - - -/* Pool class MV */ - - -DEFINE_POOL_CLASS(MVPoolClass, this) -{ - INHERIT_CLASS(this, AbstractBufferPoolClass); - this->name = "MV"; - this->size = sizeof(MVStruct); - this->offset = offsetof(MVStruct, poolStruct); - this->varargs = MVVarargs; - this->init = MVInit; - this->finish = MVFinish; - this->alloc = MVAlloc; - this->free = MVFree; - this->totalSize = MVTotalSize; - this->freeSize = MVFreeSize; - this->describe = MVDescribe; - AVERT(PoolClass, this); -} - - -MVPoolClass PoolClassMV(void) -{ - return EnsureMVPoolClass(); -} - - -/* Pool class MVDebug */ - -DEFINE_POOL_CLASS(MVDebugPoolClass, this) -{ - INHERIT_CLASS(this, MVPoolClass); - PoolClassMixInDebug(this); - this->name = "MVDBG"; - this->size = sizeof(MVDebugStruct); - this->varargs = MVDebugVarargs; - this->debugMixin = MVDebugMixin; - AVERT(PoolClass, this); -} - - -/* class functions - * - * Note this is an MPS interface extension - */ - -mps_pool_class_t mps_class_mv(void) -{ - return (mps_pool_class_t)(EnsureMVPoolClass()); -} - -mps_pool_class_t mps_class_mv_debug(void) -{ - return (mps_pool_class_t)(EnsureMVDebugPoolClass()); -} - - -/* MVCheck -- check the consistency of an MV structure */ - -Bool MVCheck(MV mv) -{ - CHECKS(MV, mv); - CHECKD(Pool, MVPool(mv)); - CHECKL(IsSubclassPoly(MVPool(mv)->class, EnsureMVPoolClass())); - CHECKD(MFS, &mv->blockPoolStruct); - CHECKD(MFS, &mv->spanPoolStruct); - CHECKL(mv->extendBy > 0); - CHECKL(mv->avgSize > 0); - CHECKL(mv->extendBy >= mv->avgSize); - /* TODO: More checks are possible. Consider what else could be checked. */ - return TRUE; -} - - -/* 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/mps/code/poolmv2.c b/mps/code/poolmv2.c index 95c3e9ecc9a..2e9046e5dd4 100644 --- a/mps/code/poolmv2.c +++ b/mps/code/poolmv2.c @@ -1,7 +1,7 @@ /* poolmv2.c: MANUAL VARIABLE-SIZED TEMPORAL POOL * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * * .purpose: A manual-variable pool designed to take advantage of * placement according to predicted deathtime. @@ -31,19 +31,17 @@ SRCID(poolmv2, "$Id$"); typedef struct MVTStruct *MVT; static void MVTVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs); -static Res MVTInit(Pool pool, ArgList arg); +static Res MVTInit(Pool pool, Arena arena, PoolClass klass, ArgList arg); static Bool MVTCheck(MVT mvt); -static void MVTFinish(Pool pool); +static void MVTFinish(Inst inst); static Res MVTBufferFill(Addr *baseReturn, Addr *limitReturn, - Pool pool, Buffer buffer, Size minSize, - Bool withReservoirPermit); -static void MVTBufferEmpty(Pool pool, Buffer buffer, Addr base, Addr limit); + Pool pool, Buffer buffer, Size minSize); +static void MVTBufferEmpty(Pool pool, Buffer buffer); static void MVTFree(Pool pool, Addr base, Size size); -static Res MVTDescribe(Pool pool, mps_lib_FILE *stream, Count depth); +static Res MVTDescribe(Inst inst, 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); +static Res MVTSegAlloc(Seg *segReturn, MVT mvt, Size size); static void MVTSegFree(MVT mvt, Seg seg); static Bool MVTReturnSegs(MVT mvt, Range range, Arena arena); @@ -58,6 +56,9 @@ static Land MVTFreePrimary(MVT mvt); static Land MVTFreeSecondary(MVT mvt); static Land MVTFreeLand(MVT mvt); +typedef MVT MVTPool; +DECLARE_CLASS(Pool, MVTPool, AbstractBufferPool); + /* Types */ @@ -94,63 +95,61 @@ typedef struct MVTStruct Size unavailable; /* bytes lost to fragmentation */ /* pool meters*/ - METER_DECL(segAllocs); - METER_DECL(segFrees); - METER_DECL(bufferFills); - METER_DECL(bufferEmpties); - METER_DECL(poolFrees); - METER_DECL(poolSize); - METER_DECL(poolAllocated); - METER_DECL(poolAvailable); - METER_DECL(poolUnavailable); - METER_DECL(poolUtilization); + METER_DECL(segAllocs) + METER_DECL(segFrees) + METER_DECL(bufferFills) + METER_DECL(bufferEmpties) + METER_DECL(poolFrees) + METER_DECL(poolSize) + METER_DECL(poolAllocated) + METER_DECL(poolAvailable) + METER_DECL(poolUnavailable) + METER_DECL(poolUtilization) /* abq meters */ - METER_DECL(finds); - METER_DECL(overflows); - METER_DECL(underflows); - METER_DECL(refills); - METER_DECL(refillPushes); - METER_DECL(returns); + METER_DECL(finds) + METER_DECL(overflows) + METER_DECL(underflows) + METER_DECL(refills) + METER_DECL(refillPushes) + METER_DECL(returns) /* fragmentation meters */ - METER_DECL(perfectFits); - METER_DECL(firstFits); - METER_DECL(secondFits); - METER_DECL(failures); + METER_DECL(perfectFits) + METER_DECL(firstFits) + METER_DECL(secondFits) + METER_DECL(failures) /* contingency meters */ - METER_DECL(emergencyContingencies); - METER_DECL(fragLimitContingencies); - METER_DECL(contingencySearches); - METER_DECL(contingencyHardSearches); + METER_DECL(emergencyContingencies) + METER_DECL(fragLimitContingencies) + METER_DECL(contingencySearches) + METER_DECL(contingencyHardSearches) /* splinter meters */ - METER_DECL(splinters); - METER_DECL(splintersUsed); - METER_DECL(splintersDropped); - METER_DECL(sawdust); + METER_DECL(splinters) + METER_DECL(splintersUsed) + METER_DECL(splintersDropped) + METER_DECL(sawdust) /* exception meters */ - METER_DECL(exceptions); - METER_DECL(exceptionSplinters); - METER_DECL(exceptionReturns); + METER_DECL(exceptions) + METER_DECL(exceptionSplinters) + METER_DECL(exceptionReturns) Sig sig; } MVTStruct; -DEFINE_POOL_CLASS(MVTPoolClass, this) +DEFINE_CLASS(Pool, MVTPool, klass) { - INHERIT_CLASS(this, AbstractBufferPoolClass); - this->name = "MVT"; - this->size = sizeof(MVTStruct); - this->offset = offsetof(MVTStruct, poolStruct); - 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); + INHERIT_CLASS(klass, MVTPool, AbstractBufferPool); + klass->instClassStruct.describe = MVTDescribe; + klass->instClassStruct.finish = MVTFinish; + klass->size = sizeof(MVTStruct); + klass->varargs = MVTVarargs; + klass->init = MVTInit; + klass->free = MVTFree; + klass->bufferFill = MVTBufferFill; + klass->bufferEmpty = MVTBufferEmpty; + klass->totalSize = MVTTotalSize; + klass->freeSize = MVTFreeSize; + AVERT(PoolClass, klass); } /* Macros */ @@ -221,9 +220,8 @@ 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) +static Res MVTInit(Pool pool, Arena arena, PoolClass klass, ArgList args) { - Arena arena; Size align = MVT_ALIGN_DEFAULT; Size minSize = MVT_MIN_SIZE_DEFAULT; Size meanSize = MVT_MEAN_SIZE_DEFAULT; @@ -236,12 +234,11 @@ static Res MVTInit(Pool pool, ArgList args) Res res; ArgStruct arg; - AVERT(Pool, pool); - mvt = PoolMVT(pool); - /* can't AVERT mvt, yet */ - arena = PoolArena(pool); + AVER(pool != NULL); AVERT(Arena, arena); - + AVERT(ArgList, args); + UNUSED(klass); /* used for debug pools only */ + if (ArgPick(&arg, args, MPS_KEY_ALIGN)) align = arg.val.align; if (ArgPick(&arg, args, MPS_KEY_MIN_SIZE)) @@ -261,10 +258,10 @@ static Res MVTInit(Pool pool, ArgList args) 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 . - */ + of a Freelist to store the free address ranges in low-memory + situations. See . */ AVER(AlignIsAligned(align, FreelistMinimumAlignment)); + AVER(align <= ArenaGrainSize(arena)); AVER(0 < minSize); AVER(minSize <= meanSize); AVER(meanSize <= maxSize); @@ -281,12 +278,17 @@ static Res MVTInit(Pool pool, ArgList args) if (abqDepth < 3) abqDepth = 3; - res = LandInit(MVTFreePrimary(mvt), CBSFastLandClassGet(), arena, align, mvt, + res = NextMethod(Pool, MVTPool, init)(pool, arena, klass, args); + if (res != ResOK) + goto failNextInit; + mvt = CouldBeA(MVTPool, pool); + + res = LandInit(MVTFreePrimary(mvt), CLASS(CBSFast), arena, align, mvt, mps_args_none); if (res != ResOK) goto failFreePrimaryInit; - res = LandInit(MVTFreeSecondary(mvt), FreelistLandClassGet(), arena, align, + res = LandInit(MVTFreeSecondary(mvt), CLASS(Freelist), arena, align, mvt, mps_args_none); if (res != ResOK) goto failFreeSecondaryInit; @@ -294,7 +296,7 @@ static Res MVTInit(Pool pool, ArgList args) 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, + res = LandInit(MVTFreeLand(mvt), CLASS(Failover), arena, align, mvt, foArgs); } MPS_ARGS_END(foArgs); if (res != ResOK) @@ -305,6 +307,7 @@ static Res MVTInit(Pool pool, ArgList args) goto failABQInit; pool->alignment = align; + pool->alignShift = SizeLog2(pool->alignment); mvt->reuseSize = reuseSize; mvt->fillSize = fillSize; mvt->abqOverflow = FALSE; @@ -359,11 +362,13 @@ static Res MVTInit(Pool pool, ArgList args) METER_INIT(mvt->exceptionSplinters, "exception splinters", (void *)mvt); METER_INIT(mvt->exceptionReturns, "exception returns", (void *)mvt); + SetClassOfPoly(pool, CLASS(MVTPool)); mvt->sig = MVTSig; - - AVERT(MVT, mvt); + AVERC(MVT, mvt); + EVENT6(PoolInitMVT, pool, minSize, meanSize, maxSize, reserveDepth, fragLimit); + return ResOK; failABQInit: @@ -373,6 +378,8 @@ static Res MVTInit(Pool pool, ArgList args) failFreeSecondaryInit: LandFinish(MVTFreePrimary(mvt)); failFreePrimaryInit: + NextMethod(Inst, MVTPool, finish)(MustBeA(Inst, pool)); +failNextInit: AVER(res != ResOK); return res; } @@ -384,8 +391,9 @@ ATTRIBUTE_UNUSED static Bool MVTCheck(MVT mvt) { CHECKS(MVT, mvt); + CHECKC(MVTPool, mvt); CHECKD(Pool, MVTPool(mvt)); - CHECKL(MVTPool(mvt)->class == MVTPoolClassGet()); + CHECKC(MVTPool, mvt); CHECKD(CBS, &mvt->cbsStruct); CHECKD(ABQ, &mvt->abqStruct); CHECKD(Freelist, &mvt->flStruct); @@ -415,18 +423,15 @@ static Bool MVTCheck(MVT mvt) /* MVTFinish -- finish an MVT pool */ -static void MVTFinish(Pool pool) +static void MVTFinish(Inst inst) { - MVT mvt; - Arena arena; + Pool pool = MustBeA(AbstractPool, inst); + MVT mvt = MustBeA(MVTPool, pool); + Arena arena = PoolArena(pool); Ring ring; Ring node, nextNode; - AVERT(Pool, pool); - mvt = PoolMVT(pool); AVERT(MVT, mvt); - arena = PoolArena(pool); - AVERT(Arena, arena); mvt->sig = SigInvalid; @@ -444,6 +449,8 @@ static void MVTFinish(Pool pool) LandFinish(MVTFreeLand(mvt)); LandFinish(MVTFreeSecondary(mvt)); LandFinish(MVTFreePrimary(mvt)); + + NextMethod(Inst, MVTPool, finish)(inst); } @@ -469,7 +476,8 @@ static void MVTFinish(Pool pool) /* MVTNoteFill -- record that a buffer fill has occurred */ -static void MVTNoteFill(MVT mvt, Addr base, Addr limit, Size minSize) { +static void MVTNoteFill(MVT mvt, Addr base, Addr limit, Size minSize) +{ mvt->available -= AddrOffset(base, limit); mvt->allocated += AddrOffset(base, limit); AVER(mvt->size == mvt->allocated + mvt->available + mvt->unavailable); @@ -491,8 +499,7 @@ static void MVTNoteFill(MVT mvt, Addr base, Addr limit, Size minSize) { static Res MVTOversizeFill(Addr *baseReturn, Addr *limitReturn, MVT mvt, - Size minSize, - Bool withReservoirPermit) + Size minSize) { Res res; Seg seg; @@ -501,7 +508,7 @@ static Res MVTOversizeFill(Addr *baseReturn, alignedSize = SizeArenaGrains(minSize, PoolArena(MVTPool(mvt))); - res = MVTSegAlloc(&seg, mvt, alignedSize, withReservoirPermit); + res = MVTSegAlloc(&seg, mvt, alignedSize); if (res != ResOK) return res; @@ -567,7 +574,7 @@ 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 "may be used uninitialized" */ Arena arena; base = *baseIO; @@ -660,14 +667,13 @@ static Bool MVTContingencyFill(Addr *baseReturn, Addr *limitReturn, static Res MVTSegFill(Addr *baseReturn, Addr *limitReturn, MVT mvt, Size fillSize, - Size minSize, - Bool withReservoirPermit) + Size minSize) { Res res; Seg seg; Addr base, limit; - res = MVTSegAlloc(&seg, mvt, fillSize, withReservoirPermit); + res = MVTSegAlloc(&seg, mvt, fillSize); if (res != ResOK) return res; @@ -686,8 +692,7 @@ static Res MVTSegFill(Addr *baseReturn, Addr *limitReturn, * See */ static Res MVTBufferFill(Addr *baseReturn, Addr *limitReturn, - Pool pool, Buffer buffer, Size minSize, - Bool withReservoirPermit) + Pool pool, Buffer buffer, Size minSize) { MVT mvt; Res res; @@ -701,13 +706,12 @@ static Res MVTBufferFill(Addr *baseReturn, Addr *limitReturn, AVER(BufferIsReset(buffer)); AVER(minSize > 0); AVER(SizeIsAligned(minSize, pool->alignment)); - AVERT(Bool, withReservoirPermit); /* Allocate oversize blocks exactly, directly from the arena. */ if (minSize > mvt->fillSize) { return MVTOversizeFill(baseReturn, limitReturn, mvt, - minSize, withReservoirPermit); + minSize); } /* Use any splinter, if available. @@ -732,7 +736,7 @@ static Res MVTBufferFill(Addr *baseReturn, Addr *limitReturn, /* Attempt to request a block from the arena. */ res = MVTSegFill(baseReturn, limitReturn, - mvt, mvt->fillSize, minSize, withReservoirPermit); + mvt, mvt->fillSize, minSize); if (res == ResOK) return ResOK; @@ -751,23 +755,21 @@ static Res MVTBufferFill(Addr *baseReturn, Addr *limitReturn, /* MVTDeleteOverlapping -- ABQIterate callback used by MVTInsert and - * MVTDelete. It receives a Range in its closureP argument, and sets + * MVTDelete. It receives a Range in its closure argument, and sets * *deleteReturn to TRUE for ranges in the ABQ that overlap with it, * and FALSE for ranges that do not. */ static Bool MVTDeleteOverlapping(Bool *deleteReturn, void *element, - void *closureP, Size closureS) + void *closure) { Range oldRange, newRange; AVER(deleteReturn != NULL); AVER(element != NULL); - AVER(closureP != NULL); - AVER(closureS == UNUSED_SIZE); - UNUSED(closureS); + AVER(closure != NULL); oldRange = element; - newRange = closureP; + newRange = closure; *deleteReturn = RangesOverlap(oldRange, newRange); return TRUE; @@ -830,7 +832,7 @@ 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, UNUSED_SIZE); + ABQIterate(MVTABQ(mvt), MVTDeleteOverlapping, &newRange); (void)MVTReserve(mvt, &newRange); } @@ -859,7 +861,7 @@ 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, UNUSED_SIZE); + ABQIterate(MVTABQ(mvt), MVTDeleteOverlapping, &rangeOld); /* 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. @@ -881,18 +883,20 @@ static Res MVTDelete(MVT mvt, Addr base, Addr limit) * * See */ -static void MVTBufferEmpty(Pool pool, Buffer buffer, - Addr base, Addr limit) +static void MVTBufferEmpty(Pool pool, Buffer buffer) { MVT mvt; Size size; Res res; + Addr base, limit; AVERT(Pool, pool); mvt = PoolMVT(pool); AVERT(MVT, mvt); AVERT(Buffer, buffer); AVER(BufferIsReady(buffer)); + base = BufferGetInit(buffer); + limit = BufferLimit(buffer); AVER(base <= limit); size = AddrOffset(base, limit); @@ -975,7 +979,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 "may be used uninitialized" */ SURELY(SegOfAddr(&seg, PoolArena(pool), base)); AVER(base == SegBase(seg)); AVER(limit <= SegLimit(seg)); @@ -1022,36 +1026,37 @@ static Size MVTFreeSize(Pool pool) /* MVTDescribe -- describe an MVT pool */ -static Res MVTDescribe(Pool pool, mps_lib_FILE *stream, Count depth) +static Res MVTDescribe(Inst inst, mps_lib_FILE *stream, Count depth) { + Pool pool = CouldBeA(AbstractPool, inst); + MVT mvt = CouldBeA(MVTPool, pool); Res res; - MVT mvt; - if (!TESTT(Pool, pool)) - return ResFAIL; - mvt = PoolMVT(pool); - if (!TESTT(MVT, mvt)) - return ResFAIL; + if (!TESTC(MVTPool, mvt)) + return ResPARAM; if (stream == NULL) - return ResFAIL; + return ResPARAM; - 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, + res = NextMethod(Inst, MVTPool, describe)(inst, stream, depth); + if (res != ResOK) + return res; + + res = WriteF(stream, depth + 2, + "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; @@ -1102,8 +1107,7 @@ static Res MVTDescribe(Pool pool, mps_lib_FILE *stream, Count depth) 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; + return ResOK; } @@ -1114,7 +1118,7 @@ static Res MVTDescribe(Pool pool, mps_lib_FILE *stream, Count depth) PoolClass PoolClassMVT(void) { - return MVTPoolClassGet(); + return CLASS(MVTPool); } @@ -1135,11 +1139,10 @@ mps_pool_class_t mps_class_mvt(void) /* MVTSegAlloc -- encapsulates SegAlloc with associated accounting and * metering */ -static Res MVTSegAlloc(Seg *segReturn, MVT mvt, Size size, - Bool withReservoirPermit) +static Res MVTSegAlloc(Seg *segReturn, MVT mvt, Size size) { - Res res = SegAlloc(segReturn, SegClassGet(), LocusPrefDefault(), size, - MVTPool(mvt), withReservoirPermit, argsNone); + Res res = SegAlloc(segReturn, CLASS(Seg), LocusPrefDefault(), size, + MVTPool(mvt), argsNone); if (res == ResOK) { Size segSize = SegSize(*segReturn); @@ -1187,7 +1190,7 @@ static Bool MVTReturnSegs(MVT mvt, Range range, Arena arena) limit = RangeLimit(range); while (base < limit) { - Seg seg; + Seg seg = NULL; /* suppress "may be used uninitialized" */ Addr segBase, segLimit; SURELY(SegOfAddr(&seg, arena, base)); @@ -1210,15 +1213,13 @@ static Bool MVTReturnSegs(MVT mvt, Range range, Arena arena) */ static Bool MVTRefillVisitor(Land land, Range range, - void *closureP, Size closureS) + void *closure) { MVT mvt; AVERT(Land, land); - mvt = closureP; + mvt = closure; AVERT(MVT, mvt); - AVER(closureS == UNUSED_SIZE); - UNUSED(closureS); if (RangeSize(range) < mvt->reuseSize) return TRUE; @@ -1241,7 +1242,7 @@ static void MVTRefillABQIfEmpty(MVT mvt, Size size) mvt->abqOverflow = FALSE; METER_ACC(mvt->refills, size); /* The iteration stops if the ABQ overflows, so may finish or not. */ - (void)LandIterate(MVTFreeLand(mvt), MVTRefillVisitor, mvt, UNUSED_SIZE); + (void)LandIterate(MVTFreeLand(mvt), MVTRefillVisitor, mvt); } } @@ -1260,7 +1261,7 @@ typedef struct MVTContigencyClosureStruct } MVTContigencyClosureStruct, *MVTContigencyClosure; static Bool MVTContingencyVisitor(Land land, Range range, - void *closureP, Size closureS) + void *closure) { MVT mvt; Size size; @@ -1269,12 +1270,10 @@ static Bool MVTContingencyVisitor(Land land, Range range, AVERT(Land, land); AVERT(Range, range); - AVER(closureP != NULL); - cl = closureP; + AVER(closure != NULL); + cl = closure; mvt = cl->mvt; AVERT(MVT, mvt); - AVER(closureS == UNUSED_SIZE); - UNUSED(closureS); base = RangeBase(range); limit = RangeLimit(range); @@ -1312,7 +1311,7 @@ static Bool MVTContingencySearch(Addr *baseReturn, Addr *limitReturn, cls.steps = 0; cls.hardSteps = 0; - if (LandIterate(MVTFreeLand(mvt), MVTContingencyVisitor, &cls, UNUSED_SIZE)) + if (LandIterate(MVTFreeLand(mvt), MVTContingencyVisitor, &cls)) return FALSE; AVER(RangeSize(&cls.range) >= min); @@ -1332,7 +1331,7 @@ static Bool MVTContingencySearch(Addr *baseReturn, Addr *limitReturn, static Bool MVTCheckFit(Addr base, Addr limit, Size min, Arena arena) { - Seg seg; + Seg seg = NULL; /* suppress "may be used uninitialized" */ Addr segLimit; SURELY(SegOfAddr(&seg, arena, base)); @@ -1357,23 +1356,9 @@ 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 Land _mps_mvt_cbs(Pool); -Land _mps_mvt_cbs(Pool pool) { - MVT mvt; - - AVERT(Pool, pool); - mvt = PoolMVT(pool); - AVERT(MVT, mvt); - - return MVTFreePrimary(mvt); -} - - /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/poolmvff.c b/mps/code/poolmvff.c index 22b6d004f68..8f1f3e7cb89 100644 --- a/mps/code/poolmvff.c +++ b/mps/code/poolmvff.c @@ -1,21 +1,21 @@ /* poolmvff.c: First Fit Manual Variable Pool * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 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 + * pool, since it is used to implement that pool. + * * .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. + * variable size where address-ordered first (or last) fit is an + * appropriate policy. * * .design: * - * NOTE - * - * 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 - * + * .critical: In manual-allocation-bound programs using MVFF, many of + * these functions are on the critical paths via mps_alloc (and then + * PoolAlloc, MVFFAlloc) and mps_free (and then PoolFree, MVFFFree). */ #include "cbs.h" @@ -24,53 +24,30 @@ #include "freelist.h" #include "mpm.h" #include "mpscmvff.h" +#include "poolmvff.h" #include "mpscmfs.h" #include "poolmfs.h" SRCID(poolmvff, "$Id$"); -/* Would go in poolmvff.h if the class had any MPS-internal clients. */ -extern PoolClass PoolClassMVFF(void); +/* Note: MVFFStruct is declared in mpmst.h rather than here because it + is the control pool and is inlined in the arena globals. */ - -/* MVFFStruct -- MVFF (Manual Variable First Fit) pool outer structure - * - * The signature is placed at the end, see - * - */ - -#define MVFFSig ((Sig)0x5193FFF9) /* SIGnature MVFF */ - -typedef struct MVFFStruct *MVFF; -typedef struct MVFFStruct { /* MVFF pool outer structure */ - PoolStruct poolStruct; /* generic structure */ - LocusPrefStruct locusPrefStruct; /* the preferences for allocation */ - Size extendBy; /* size to extend pool by */ - Size avgSize; /* client estimate of allocation size */ - 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; +typedef MVFF MVFFPool; +#define MVFFPoolCheck MVFFCheck +DECLARE_CLASS(Pool, MVFFPool, AbstractBufferPool); +DECLARE_CLASS(Pool, MVFFDebugPool, MVFFPool); #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 MVFFTotalLand(mvff) (&(mvff)->totalCBSStruct.landStruct) +#define MVFFFreePrimary(mvff) (&(mvff)->freeCBSStruct.landStruct) #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); - /* MVFFDebug -- MVFFDebug class */ @@ -97,10 +74,21 @@ static void MVFFReduce(MVFF mvff) Size freeSize, freeLimit, targetFree; RangeStruct freeRange, oldFreeRange; Align grainSize; + Land totalLand, freeLand; - AVERT(MVFF, mvff); + AVERT_CRITICAL(MVFF, mvff); arena = PoolArena(MVFFPool(mvff)); + /* Try to return memory when the amount of free memory exceeds a + threshold fraction of the total memory. */ + + totalLand = MVFFTotalLand(mvff); + freeLimit = (Size)(LandSize(totalLand) * mvff->spare); + freeLand = MVFFFreeLand(mvff); + freeSize = LandSize(freeLand); + if (freeSize < freeLimit) + return; + /* 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 @@ -109,14 +97,6 @@ static void MVFFReduce(MVFF mvff) grainSize = ArenaGrainSize(arena); - /* 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; - /* For hysteresis, return only a proportion of the free memory. */ targetFree = freeLimit / 2; @@ -130,7 +110,7 @@ static void MVFFReduce(MVFF mvff) stored at the root node. */ while (freeSize > targetFree - && LandFindLargest(&freeRange, &oldFreeRange, MVFFFreeLand(mvff), + && LandFindLargest(&freeRange, &oldFreeRange, freeLand, grainSize, FindDeleteNONE)) { RangeStruct grainRange, oldRange; @@ -168,16 +148,16 @@ static void MVFFReduce(MVFF mvff) to delete from the TotalCBS we add back to the free list, which can't fail. */ - res = LandDelete(&oldRange, MVFFFreeLand(mvff), &grainRange); + res = LandDelete(&oldRange, freeLand, &grainRange); if (res != ResOK) break; freeSize -= RangeSize(&grainRange); - AVER(freeSize == LandSize(MVFFFreeLand(mvff))); + AVER(freeSize == LandSize(freeLand)); - res = LandDelete(&oldRange, MVFFTotalLand(mvff), &grainRange); + res = LandDelete(&oldRange, totalLand, &grainRange); if (res != ResOK) { RangeStruct coalescedRange; - res = LandInsert(&coalescedRange, MVFFFreeLand(mvff), &grainRange); + res = LandInsert(&coalescedRange, freeLand, &grainRange); AVER(res == ResOK); break; } @@ -189,13 +169,11 @@ static void MVFFReduce(MVFF mvff) /* MVFFExtend -- allocate a new range from the arena * - * 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. + * Allocate a new range from the arena of at least the specified + * size. The specified size should be pool-aligned. Add it to the + * allocated and free lists. */ -static Res MVFFExtend(Range rangeReturn, MVFF mvff, Size size, - Bool withReservoirPermit) +static Res MVFFExtend(Range rangeReturn, MVFF mvff, Size size) { Pool pool; Arena arena; @@ -203,10 +181,10 @@ static Res MVFFExtend(Range rangeReturn, MVFF mvff, Size size, RangeStruct range, coalescedRange; Addr base; Res res; + Land totalLand, freeLand; AVERT(MVFF, mvff); AVER(size > 0); - AVERT(Bool, withReservoirPermit); pool = MVFFPool(mvff); arena = PoolArena(pool); @@ -222,20 +200,19 @@ static Res MVFFExtend(Range rangeReturn, MVFF mvff, Size size, allocSize = SizeArenaGrains(allocSize, arena); - res = ArenaAlloc(&base, MVFFLocusPref(mvff), allocSize, pool, - withReservoirPermit); + res = ArenaAlloc(&base, MVFFLocusPref(mvff), allocSize, pool); if (res != ResOK) { /* try again with a range just large enough for object */ /* see */ allocSize = SizeArenaGrains(size, arena); - res = ArenaAlloc(&base, MVFFLocusPref(mvff), allocSize, pool, - withReservoirPermit); + res = ArenaAlloc(&base, MVFFLocusPref(mvff), allocSize, pool); if (res != ResOK) return res; } RangeInitSize(&range, base, allocSize); - res = LandInsert(&coalescedRange, MVFFTotalLand(mvff), &range); + totalLand = MVFFTotalLand(mvff); + res = LandInsert(&coalescedRange, totalLand, &range); if (res != ResOK) { /* Can't record this memory, so return it to the arena and fail. */ ArenaFree(base, allocSize, pool); @@ -243,7 +220,8 @@ static Res MVFFExtend(Range rangeReturn, MVFF mvff, Size size, } DebugPoolFreeSplat(pool, RangeBase(&range), RangeLimit(&range)); - res = LandInsert(rangeReturn, MVFFFreeLand(mvff), &range); + freeLand = MVFFFreeLand(mvff); + res = LandInsert(rangeReturn, freeLand, &range); /* Insertion must succeed because it fails over to a Freelist. */ AVER(res == ResOK); @@ -262,37 +240,35 @@ static Res MVFFExtend(Range rangeReturn, MVFF mvff, Size size, * If there is no suitable free block, try extending the pool. */ static Res mvffFindFree(Range rangeReturn, MVFF mvff, Size size, - LandFindMethod findMethod, FindDelete findDelete, - Bool withReservoirPermit) + LandFindMethod findMethod, FindDelete findDelete) { Bool found; RangeStruct oldRange; Land land; - AVER(rangeReturn != NULL); - AVERT(MVFF, mvff); - AVER(size > 0); - AVER(SizeIsAligned(size, PoolAlignment(MVFFPool(mvff)))); - AVER(FUNCHECK(findMethod)); - AVERT(FindDelete, findDelete); - AVERT(Bool, withReservoirPermit); + AVER_CRITICAL(rangeReturn != NULL); + AVERT_CRITICAL(MVFF, mvff); + AVER_CRITICAL(size > 0); + AVER_CRITICAL(SizeIsAligned(size, PoolAlignment(MVFFPool(mvff)))); + AVER_CRITICAL(FUNCHECK(findMethod)); + AVERT_CRITICAL(FindDelete, findDelete); land = MVFFFreeLand(mvff); found = (*findMethod)(rangeReturn, &oldRange, land, size, findDelete); if (!found) { RangeStruct newRange; Res res; - res = MVFFExtend(&newRange, mvff, size, withReservoirPermit); + res = MVFFExtend(&newRange, mvff, size); if (res != ResOK) return res; found = (*findMethod)(rangeReturn, &oldRange, land, size, findDelete); /* 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_CRITICAL(found); + AVER_CRITICAL(RangesOverlap(rangeReturn, &newRange)); } - AVER(found); + AVER_CRITICAL(found); return ResOK; } @@ -300,8 +276,7 @@ static Res mvffFindFree(Range rangeReturn, MVFF mvff, Size size, /* MVFFAlloc -- Allocate a block */ -static Res MVFFAlloc(Addr *aReturn, Pool pool, Size size, - Bool withReservoirPermit) +static Res MVFFAlloc(Addr *aReturn, Pool pool, Size size) { Res res; MVFF mvff; @@ -309,23 +284,21 @@ static Res MVFFAlloc(Addr *aReturn, Pool pool, Size size, LandFindMethod findMethod; FindDelete findDelete; - AVER(aReturn != NULL); - AVERT(Pool, pool); + AVER_CRITICAL(aReturn != NULL); + AVERT_CRITICAL(Pool, pool); mvff = PoolMVFF(pool); - AVERT(MVFF, mvff); - AVER(size > 0); - AVERT(Bool, withReservoirPermit); + AVERT_CRITICAL(MVFF, mvff); + AVER_CRITICAL(size > 0); size = SizeAlignUp(size, PoolAlignment(pool)); findMethod = mvff->firstFit ? LandFindFirst : LandFindLast; findDelete = mvff->slotHigh ? FindDeleteHIGH : FindDeleteLOW; - res = mvffFindFree(&range, mvff, size, findMethod, findDelete, - withReservoirPermit); + res = mvffFindFree(&range, mvff, size, findMethod, findDelete); if (res != ResOK) return res; - AVER(RangeSize(&range) == size); + AVER_CRITICAL(RangeSize(&range) == size); *aReturn = RangeBase(&range); return ResOK; } @@ -338,19 +311,21 @@ static void MVFFFree(Pool pool, Addr old, Size size) Res res; RangeStruct range, coalescedRange; MVFF mvff; + Land freeLand; - AVERT(Pool, pool); + AVERT_CRITICAL(Pool, pool); mvff = PoolMVFF(pool); - AVERT(MVFF, mvff); + AVERT_CRITICAL(MVFF, mvff); - AVER(old != (Addr)0); - AVER(AddrIsAligned(old, PoolAlignment(pool))); - AVER(size > 0); + AVER_CRITICAL(old != (Addr)0); + AVER_CRITICAL(AddrIsAligned(old, PoolAlignment(pool))); + AVER_CRITICAL(size > 0); RangeInitSize(&range, old, SizeAlignUp(size, PoolAlignment(pool))); - res = LandInsert(&coalescedRange, MVFFFreeLand(mvff), &range); + freeLand = MVFFFreeLand(mvff); + res = LandInsert(&coalescedRange, freeLand, &range); /* Insertion must succeed because it fails over to a Freelist. */ - AVER(res == ResOK); + AVER_CRITICAL(res == ResOK); MVFFReduce(mvff); } @@ -361,8 +336,7 @@ static void MVFFFree(Pool pool, Addr old, Size size) * allocation policy; see . */ static Res MVFFBufferFill(Addr *baseReturn, Addr *limitReturn, - Pool pool, Buffer buffer, Size size, - Bool withReservoirPermit) + Pool pool, Buffer buffer, Size size) { Res res; MVFF mvff; @@ -376,10 +350,8 @@ static Res MVFFBufferFill(Addr *baseReturn, Addr *limitReturn, AVERT(Buffer, buffer); AVER(size > 0); AVER(SizeIsAligned(size, PoolAlignment(pool))); - AVERT(Bool, withReservoirPermit); - res = mvffFindFree(&range, mvff, size, LandFindLargest, FindDeleteENTIRE, - withReservoirPermit); + res = mvffFindFree(&range, mvff, size, LandFindLargest, FindDeleteENTIRE); if (res != ResOK) return res; AVER(RangeSize(&range) >= size); @@ -390,31 +362,6 @@ static Res MVFFBufferFill(Addr *baseReturn, Addr *limitReturn, } -/* MVFFBufferEmpty -- return unused portion of this buffer */ - -static void MVFFBufferEmpty(Pool pool, Buffer buffer, - Addr base, Addr limit) -{ - Res res; - MVFF mvff; - RangeStruct range, coalescedRange; - - AVERT(Pool, pool); - mvff = PoolMVFF(pool); - AVERT(MVFF, mvff); - AVERT(Buffer, buffer); - AVER(BufferIsReady(buffer)); - RangeInit(&range, base, limit); - - if (RangeIsEmpty(&range)) - return; - - res = LandInsert(&coalescedRange, MVFFFreeLand(mvff), &range); - AVER(res == ResOK); - MVFFReduce(mvff); -} - - /* MVFFVarargs -- decode obsolete varargs */ static void MVFFVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) @@ -449,7 +396,7 @@ 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) +static Res MVFFInit(Pool pool, Arena arena, PoolClass klass, ArgList args) { Size extendBy = MVFF_EXTEND_BY_DEFAULT; Size avgSize = MVFF_AVG_SIZE_DEFAULT; @@ -459,12 +406,13 @@ static Res MVFFInit(Pool pool, ArgList args) Bool firstFit = MVFF_FIRST_FIT_DEFAULT; double spare = MVFF_SPARE_DEFAULT; MVFF mvff; - Arena arena; Res res; ArgStruct arg; - AVERT(Pool, pool); - arena = PoolArena(pool); + AVER(pool != NULL); + AVERT(Arena, arena); + AVERT(ArgList, args); + AVERC(PoolClass, klass); /* .arg: class-specific additional arguments; see */ /* */ @@ -499,21 +447,25 @@ static Res MVFFInit(Pool pool, ArgList args) 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. . - */ + of a Freelist to store the free address ranges in low-memory + situations. . */ AVER(AlignIsAligned(align, FreelistMinimumAlignment)); + AVER(align <= ArenaGrainSize(arena)); AVERT(Bool, slotHigh); AVERT(Bool, arenaHigh); AVERT(Bool, firstFit); - mvff = PoolMVFF(pool); + res = NextMethod(Pool, MVFFPool, init)(pool, arena, klass, args); + if (res != ResOK) + goto failNextInit; + mvff = CouldBeA(MVFFPool, pool); mvff->extendBy = extendBy; if (extendBy < ArenaGrainSize(arena)) mvff->extendBy = ArenaGrainSize(arena); mvff->avgSize = avgSize; pool->alignment = align; + pool->alignShift = SizeLog2(pool->alignment); mvff->slotHigh = slotHigh; mvff->firstFit = firstFit; mvff->spare = spare; @@ -535,7 +487,7 @@ static Res MVFFInit(Pool pool, ArgList args) MPS_ARGS_BEGIN(liArgs) { MPS_ARGS_ADD(liArgs, CBSBlockPool, MVFFBlockPool(mvff)); - res = LandInit(MVFFTotalLand(mvff), CBSFastLandClassGet(), arena, align, + res = LandInit(MVFFTotalLand(mvff), CLASS(CBSFast), arena, align, mvff, liArgs); } MPS_ARGS_END(liArgs); if (res != ResOK) @@ -543,13 +495,13 @@ static Res MVFFInit(Pool pool, ArgList args) MPS_ARGS_BEGIN(liArgs) { MPS_ARGS_ADD(liArgs, CBSBlockPool, MVFFBlockPool(mvff)); - res = LandInit(MVFFFreePrimary(mvff), CBSFastLandClassGet(), arena, align, + res = LandInit(MVFFFreePrimary(mvff), CLASS(CBSFast), arena, align, mvff, liArgs); } MPS_ARGS_END(liArgs); if (res != ResOK) goto failFreePrimaryInit; - res = LandInit(MVFFFreeSecondary(mvff), FreelistLandClassGet(), arena, align, + res = LandInit(MVFFFreeSecondary(mvff), CLASS(Freelist), arena, align, mvff, mps_args_none); if (res != ResOK) goto failFreeSecondaryInit; @@ -557,16 +509,19 @@ static Res MVFFInit(Pool pool, ArgList args) 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, + res = LandInit(MVFFFreeLand(mvff), CLASS(Failover), arena, align, mvff, foArgs); } MPS_ARGS_END(foArgs); if (res != ResOK) goto failFreeLandInit; + SetClassOfPoly(pool, CLASS(MVFFPool)); mvff->sig = MVFFSig; - AVERT(MVFF, mvff); + AVERC(MVFFPool, mvff); + EVENT8(PoolInitMVFF, pool, arena, extendBy, avgSize, align, BOOLOF(slotHigh), BOOLOF(arenaHigh), BOOLOF(firstFit)); + return ResOK; failFreeLandInit: @@ -578,6 +533,9 @@ static Res MVFFInit(Pool pool, ArgList args) failTotalLandInit: PoolFinish(MVFFBlockPool(mvff)); failBlockPoolInit: + NextMethod(Inst, MVFFPool, finish)(MustBeA(Inst, pool)); +failNextInit: + AVER(res != ResOK); return res; } @@ -585,44 +543,43 @@ static Res MVFFInit(Pool pool, ArgList args) /* MVFFFinish -- finish method for MVFF */ static Bool mvffFinishVisitor(Bool *deleteReturn, Land land, Range range, - void *closureP, Size closureS) + void *closure) { Pool pool; AVER(deleteReturn != NULL); AVERT(Land, land); AVERT(Range, range); - AVER(closureP != NULL); - pool = closureP; + AVER(closure != NULL); + pool = closure; AVERT(Pool, pool); - AVER(closureS == UNUSED_SIZE); - UNUSED(closureS); ArenaFree(RangeBase(range), RangeSize(range), pool); *deleteReturn = TRUE; return TRUE; } -static void MVFFFinish(Pool pool) +static void MVFFFinish(Inst inst) { - MVFF mvff; + Pool pool = MustBeA(AbstractPool, inst); + MVFF mvff = MustBeA(MVFFPool, pool); Bool b; + Land totalLand; - AVERT(Pool, pool); - mvff = PoolMVFF(pool); AVERT(MVFF, mvff); mvff->sig = SigInvalid; - b = LandIterateAndDelete(MVFFTotalLand(mvff), mvffFinishVisitor, pool, - UNUSED_SIZE); + totalLand = MVFFTotalLand(mvff); + b = LandIterateAndDelete(totalLand, mvffFinishVisitor, pool); AVER(b); - AVER(LandSize(MVFFTotalLand(mvff)) == 0); + AVER(LandSize(totalLand) == 0); LandFinish(MVFFFreeLand(mvff)); LandFinish(MVFFFreeSecondary(mvff)); LandFinish(MVFFFreePrimary(mvff)); - LandFinish(MVFFTotalLand(mvff)); + LandFinish(totalLand); PoolFinish(MVFFBlockPool(mvff)); + NextMethod(Inst, MVFFPool, finish)(inst); } @@ -645,12 +602,14 @@ static PoolDebugMixin MVFFDebugMixin(Pool pool) static Size MVFFTotalSize(Pool pool) { MVFF mvff; + Land totalLand; AVERT(Pool, pool); mvff = PoolMVFF(pool); AVERT(MVFF, mvff); - return LandSize(MVFFTotalLand(mvff)); + totalLand = MVFFTotalLand(mvff); + return LandSize(totalLand); } @@ -659,39 +618,40 @@ static Size MVFFTotalSize(Pool pool) static Size MVFFFreeSize(Pool pool) { MVFF mvff; + Land freeLand; AVERT(Pool, pool); mvff = PoolMVFF(pool); AVERT(MVFF, mvff); - return LandSize(MVFFFreeLand(mvff)); + freeLand = MVFFFreeLand(mvff); + return LandSize(freeLand); } /* MVFFDescribe -- describe an MVFF pool */ -static Res MVFFDescribe(Pool pool, mps_lib_FILE *stream, Count depth) +static Res MVFFDescribe(Inst inst, mps_lib_FILE *stream, Count depth) { + Pool pool = CouldBeA(AbstractPool, inst); + MVFF mvff = CouldBeA(MVFFPool, pool); Res res; - MVFF mvff; - if (!TESTT(Pool, pool)) - return ResFAIL; - mvff = PoolMVFF(pool); - if (!TESTT(MVFF, mvff)) - return ResFAIL; + if (!TESTC(MVFFPool, mvff)) + return ResPARAM; if (stream == NULL) - return ResFAIL; + return ResPARAM; - 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, - " firstFit $U\n", (WriteFU)mvff->firstFit, - " slotHigh $U\n", (WriteFU)mvff->slotHigh, - " spare $D\n", (WriteFD)mvff->spare, + res = NextMethod(Inst, MVFFPool, describe)(inst, stream, depth); + if (res != ResOK) + return res; + + res = WriteF(stream, depth + 2, + "extendBy $W\n", (WriteFW)mvff->extendBy, + "avgSize $W\n", (WriteFW)mvff->avgSize, + "firstFit $U\n", (WriteFU)mvff->firstFit, + "slotHigh $U\n", (WriteFU)mvff->slotHigh, + "spare $D\n", (WriteFD)mvff->spare, NULL); if (res != ResOK) return res; @@ -715,49 +675,43 @@ static Res MVFFDescribe(Pool pool, mps_lib_FILE *stream, Count depth) if (res != ResOK) return res; - res = WriteF(stream, depth, "} MVFF $P\n", (WriteFP)mvff, NULL); - return res; + return ResOK; } -DEFINE_POOL_CLASS(MVFFPoolClass, this) +DEFINE_CLASS(Pool, MVFFPool, klass) { - INHERIT_CLASS(this, AbstractPoolClass); - PoolClassMixInBuffer(this); - this->name = "MVFF"; - this->size = sizeof(MVFFStruct); - this->offset = offsetof(MVFFStruct, poolStruct); - this->varargs = MVFFVarargs; - this->init = MVFFInit; - this->finish = MVFFFinish; - this->alloc = MVFFAlloc; - this->free = MVFFFree; - this->bufferFill = MVFFBufferFill; - this->bufferEmpty = MVFFBufferEmpty; - this->totalSize = MVFFTotalSize; - this->freeSize = MVFFFreeSize; - this->describe = MVFFDescribe; - AVERT(PoolClass, this); + INHERIT_CLASS(klass, MVFFPool, AbstractBufferPool); + klass->instClassStruct.describe = MVFFDescribe; + klass->instClassStruct.finish = MVFFFinish; + klass->size = sizeof(MVFFStruct); + klass->varargs = MVFFVarargs; + klass->init = MVFFInit; + klass->alloc = MVFFAlloc; + klass->free = MVFFFree; + klass->bufferFill = MVFFBufferFill; + klass->totalSize = MVFFTotalSize; + klass->freeSize = MVFFFreeSize; + AVERT(PoolClass, klass); } PoolClass PoolClassMVFF(void) { - return MVFFPoolClassGet(); + return CLASS(MVFFPool); } /* Pool class MVFFDebug */ -DEFINE_POOL_CLASS(MVFFDebugPoolClass, this) +DEFINE_CLASS(Pool, MVFFDebugPool, klass) { - INHERIT_CLASS(this, MVFFPoolClass); - PoolClassMixInDebug(this); - this->name = "MVFFDBG"; - this->size = sizeof(MVFFDebugStruct); - this->varargs = MVFFDebugVarargs; - this->debugMixin = MVFFDebugMixin; - AVERT(PoolClass, this); + INHERIT_CLASS(klass, MVFFDebugPool, MVFFPool); + PoolClassMixInDebug(klass); + klass->size = sizeof(MVFFDebugStruct); + klass->varargs = MVFFDebugVarargs; + klass->debugMixin = MVFFDebugMixin; + AVERT(PoolClass, klass); } @@ -766,23 +720,22 @@ DEFINE_POOL_CLASS(MVFFDebugPoolClass, this) mps_pool_class_t mps_class_mvff(void) { - return (mps_pool_class_t)(MVFFPoolClassGet()); + return (mps_pool_class_t)(CLASS(MVFFPool)); } mps_pool_class_t mps_class_mvff_debug(void) { - return (mps_pool_class_t)(MVFFDebugPoolClassGet()); + return (mps_pool_class_t)(CLASS(MVFFDebugPool)); } /* MVFFCheck -- check the consistency of an MVFF structure */ -ATTRIBUTE_UNUSED -static Bool MVFFCheck(MVFF mvff) +Bool MVFFCheck(MVFF mvff) { CHECKS(MVFF, mvff); + CHECKC(MVFFPool, 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 */ @@ -794,32 +747,18 @@ static Bool MVFFCheck(MVFF mvff) 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((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; } -/* Return the CBS of an MVFF pool for the benefit of fotest.c. */ - -extern Land _mps_mvff_cbs(Pool); -Land _mps_mvff_cbs(Pool pool) { - MVFF mvff; - - AVERT(Pool, pool); - mvff = PoolMVFF(pool); - AVERT(MVFF, mvff); - - return MVFFFreePrimary(mvff); -} - - /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/poolmv.h b/mps/code/poolmvff.h similarity index 78% rename from mps/code/poolmv.h rename to mps/code/poolmvff.h index 01c5b9ebd73..8c374c50709 100644 --- a/mps/code/poolmv.h +++ b/mps/code/poolmvff.h @@ -1,40 +1,38 @@ -/* poolmv.h: MANUAL VARIABLE POOL +/* poolmvff.h: First Fit Manual Variable Pool * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 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. + * .purpose: This is a pool class for manually managed objects of + * variable size where address-ordered first (or last) fit is an + * appropriate policy. * - * .mv: Manual-variable pools manage variably-sized blocks of memory - * in a flexible manner. They have higher overheads than a fixed-size - * pool. - * - * .design: See + * .design: See */ -#ifndef poolmv_h -#define poolmv_h +#ifndef poolmvff_h +#define poolmvff_h #include "mpmtypes.h" -#include "mpscmv.h" +#include "mpscmvff.h" -typedef struct MVStruct *MV; +typedef struct MVFFStruct *MVFF; -extern PoolClass PoolClassMV(void); +extern PoolClass PoolClassMVFF(void); -extern Bool MVCheck(MV mv); +extern Bool MVFFCheck(MVFF mvff); -#define MVPool(mv) (&(mv)->poolStruct) +#define MVFFPool(mvff) (&(mvff)->poolStruct) -#endif /* poolmv_h */ +#endif /* poolmvff_h */ /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/pooln.c b/mps/code/pooln.c index cfb7bc0b61d..e56168ab57f 100644 --- a/mps/code/pooln.c +++ b/mps/code/pooln.c @@ -1,7 +1,7 @@ /* pooln.c: NULL POOL CLASS * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. */ #include "pooln.h" @@ -18,6 +18,10 @@ typedef struct PoolNStruct { } PoolNStruct; +typedef PoolN NPool; +DECLARE_CLASS(Pool, NPool, AbstractPool); + + /* PoolPoolN -- get the PoolN structure from generic Pool */ #define PoolPoolN(pool) PARENT(PoolNStruct, poolStruct, pool) @@ -30,48 +34,57 @@ typedef struct PoolNStruct { /* NInit -- init method for class N */ -static Res NInit(Pool pool, ArgList args) +static Res NInit(Pool pool, Arena arena, PoolClass klass, ArgList args) { - PoolN poolN = PoolPoolN(pool); + PoolN poolN; + Res res; - UNUSED(args); - + AVER(pool != NULL); + AVERT(Arena, arena); + AVERT(ArgList, args); + UNUSED(klass); /* used for debug pools only */ + + res = NextMethod(Pool, NPool, init)(pool, arena, klass, args); + if (res != ResOK) + goto failNextInit; + poolN = CouldBeA(NPool, pool); + /* Initialize pool-specific structures. */ - AVERT(PoolN, poolN); - EVENT3(PoolInit, pool, PoolArena(pool), ClassOfPool(pool)); + SetClassOfPoly(pool, CLASS(NPool)); + AVERC(PoolN, poolN); + return ResOK; + +failNextInit: + AVER(res != ResOK); + return res; } /* NFinish -- finish method for class N */ -static void NFinish(Pool pool) +static void NFinish(Inst inst) { - PoolN poolN; - - AVERT(Pool, pool); - poolN = PoolPoolN(pool); - AVERT(PoolN, poolN); + Pool pool = MustBeA(AbstractPool, inst); + PoolN poolN = MustBeA(NPool, pool); /* Finish pool-specific structures. */ + UNUSED(poolN); + + NextMethod(Inst, NPool, finish)(inst); } /* NAlloc -- alloc method for class N */ -static Res NAlloc(Addr *pReturn, Pool pool, Size size, - Bool withReservoirPermit) +static Res NAlloc(Addr *pReturn, Pool pool, Size size) { - PoolN poolN; - - AVERT(Pool, pool); - poolN = PoolPoolN(pool); - AVERT(PoolN, poolN); + PoolN poolN = MustBeA(NPool, pool); AVER(pReturn != NULL); AVER(size > 0); - AVERT(Bool, withReservoirPermit); + UNUSED(poolN); return ResLIMIT; /* limit of nil blocks exceeded */ } @@ -81,14 +94,11 @@ static Res NAlloc(Addr *pReturn, Pool pool, Size size, static void NFree(Pool pool, Addr old, Size size) { - PoolN poolN; - - AVERT(Pool, pool); - poolN = PoolPoolN(pool); - AVERT(PoolN, poolN); + PoolN poolN = MustBeA(NPool, pool); AVER(old != (Addr)0); AVER(size > 0); + UNUSED(poolN); NOTREACHED; /* can't allocate, should never free */ } @@ -97,20 +107,16 @@ static void NFree(Pool pool, Addr old, Size size) /* NBufferFill -- buffer fill method for class N */ static Res NBufferFill(Addr *baseReturn, Addr *limitReturn, - Pool pool, Buffer buffer, Size size, - Bool withReservoirPermit) + Pool pool, Buffer buffer, Size size) { - PoolN poolN; + PoolN poolN = MustBeA(NPool, pool); - AVERT(Pool, pool); - poolN = PoolPoolN(pool); - AVERT(PoolN, poolN); AVER(baseReturn != NULL); AVER(limitReturn != NULL); AVERT(Buffer, buffer); AVER(BufferIsReset(buffer)); AVER(size > 0); - AVERT(Bool, withReservoirPermit); + UNUSED(poolN); NOTREACHED; /* can't create buffers, so shouldn't fill them */ return ResUNIMPL; @@ -119,175 +125,49 @@ static Res NBufferFill(Addr *baseReturn, Addr *limitReturn, /* NBufferEmpty -- buffer empty method for class N */ -static void NBufferEmpty(Pool pool, Buffer buffer, - Addr init, Addr limit) +static void NBufferEmpty(Pool pool, Buffer buffer) { AVERT(Pool, pool); AVERT(Buffer, buffer); AVER(BufferIsReady(buffer)); - AVER(init <= limit); - NOTREACHED; /* can't create buffers, so they shouldn't trip */ } /* NDescribe -- describe method for class N */ -static Res NDescribe(Pool pool, mps_lib_FILE *stream, Count depth) +static Res NDescribe(Inst inst, mps_lib_FILE *stream, Count depth) { - PoolN poolN; + Pool pool = CouldBeA(AbstractPool, inst); + PoolN poolN = CouldBeA(NPool, pool); + Res res; - AVERT(Pool, pool); - poolN = PoolPoolN(pool); - AVERT(PoolN, poolN); + res = NextMethod(Inst, NPool, describe)(inst, stream, depth); + if (res != ResOK) + return res; - UNUSED(stream); /* TODO: should output something here */ - UNUSED(depth); + /* This is where you'd output some information about pool fields. */ + UNUSED(poolN); return ResOK; } -/* NWhiten -- condemn method for class N */ - -static Res NWhiten(Pool pool, Trace trace, Seg seg) -{ - PoolN poolN; - - AVERT(Pool, pool); - poolN = PoolPoolN(pool); - AVERT(PoolN, poolN); - - AVERT(Trace, trace); - AVERT(Seg, seg); - - NOTREACHED; /* pool doesn't have any actions */ - - return ResUNIMPL; -} - - -/* NGrey -- greyen method for class N */ - -static void NGrey(Pool pool, Trace trace, Seg seg) -{ - PoolN poolN; - - AVERT(Pool, pool); - poolN = PoolPoolN(pool); - AVERT(PoolN, poolN); - - AVERT(Trace, trace); - AVERT(Seg, seg); -} - - -/* NBlacken -- blacken method for class N */ - -static void NBlacken(Pool pool, TraceSet traceSet, Seg seg) -{ - PoolN poolN; - - AVERT(Pool, pool); - poolN = PoolPoolN(pool); - AVERT(PoolN, poolN); - - AVERT(TraceSet, traceSet); - AVERT(Seg, seg); -} - - -/* NScan -- scan method for class N */ - -static Res NScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg) -{ - PoolN poolN; - - AVER(totalReturn != NULL); - AVERT(ScanState, ss); - AVERT(Pool, pool); - poolN = PoolPoolN(pool); - AVERT(PoolN, poolN); - AVERT(Seg, seg); - - return ResOK; -} - - -/* NFix -- fix method for class N */ - -static Res NFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) -{ - PoolN poolN; - - AVERT(Pool, pool); - poolN = PoolPoolN(pool); - AVERT(PoolN, poolN); - - AVERT(ScanState, ss); - UNUSED(refIO); - AVERT(Seg, seg); - NOTREACHED; /* Since we don't allocate any objects, should never */ - /* be called upon to fix a reference. */ - return ResFAIL; -} - - -/* NReclaim -- reclaim method for class N */ - -static void NReclaim(Pool pool, Trace trace, Seg seg) -{ - PoolN poolN; - - AVERT(Pool, pool); - poolN = PoolPoolN(pool); - AVERT(PoolN, poolN); - - AVERT(Trace, trace); - AVERT(Seg, seg); - /* all unmarked and white objects reclaimed */ -} - - -/* NTraceEnd -- trace end method for class N */ - -static void NTraceEnd(Pool pool, Trace trace) -{ - PoolN poolN; - - AVERT(Pool, pool); - poolN = PoolPoolN(pool); - AVERT(PoolN, poolN); - - AVERT(Trace, trace); -} - - /* NPoolClass -- pool class definition for N */ -DEFINE_POOL_CLASS(NPoolClass, this) +DEFINE_CLASS(Pool, NPool, klass) { - INHERIT_CLASS(this, AbstractPoolClass); - this->name = "N"; - this->size = sizeof(PoolNStruct); - this->offset = offsetof(PoolNStruct, poolStruct); - this->attr |= AttrGC; - this->init = NInit; - this->finish = NFinish; - this->alloc = NAlloc; - this->free = NFree; - this->bufferFill = NBufferFill; - this->bufferEmpty = NBufferEmpty; - this->whiten = NWhiten; - this->grey = NGrey; - this->blacken = NBlacken; - this->scan = NScan; - this->fix = NFix; - this->fixEmergency = NFix; - this->reclaim = NReclaim; - this->traceEnd = NTraceEnd; - this->describe = NDescribe; - AVERT(PoolClass, this); + INHERIT_CLASS(klass, NPool, AbstractPool); + klass->instClassStruct.describe = NDescribe; + klass->instClassStruct.finish = NFinish; + klass->size = sizeof(PoolNStruct); + klass->attr |= AttrGC; + klass->init = NInit; + klass->alloc = NAlloc; + klass->free = NFree; + klass->bufferFill = NBufferFill; + klass->bufferEmpty = NBufferEmpty; + AVERT(PoolClass, klass); } @@ -295,7 +175,7 @@ DEFINE_POOL_CLASS(NPoolClass, this) PoolClass PoolClassN(void) { - return EnsureNPoolClass(); + return CLASS(NPool); } @@ -305,7 +185,7 @@ Bool PoolNCheck(PoolN poolN) { CHECKL(poolN != NULL); CHECKD(Pool, PoolNPool(poolN)); - CHECKL(PoolNPool(poolN)->class == EnsureNPoolClass()); + CHECKC(NPool, poolN); UNUSED(poolN); /* */ return TRUE; @@ -314,7 +194,7 @@ Bool PoolNCheck(PoolN poolN) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/poolncv.c b/mps/code/poolncv.c index a3f85342cb8..1f1d08ec2fc 100644 --- a/mps/code/poolncv.c +++ b/mps/code/poolncv.c @@ -13,17 +13,17 @@ #include /* printf */ -static void testit(ArenaClass class, ArgList args) +static void testit(ArenaClass klass, ArgList args) { Arena arena; Pool pool; Res res; Addr p; - die(ArenaCreate(&arena, class, args), "ArenaCreate"); + die(ArenaCreate(&arena, klass, args), "ArenaCreate"); die(PoolCreate(&pool, arena, PoolClassN(), argsNone), "PoolNCreate"); - res = PoolAlloc(&p, pool, 1, /* withReservoirPermit */ FALSE); + res = PoolAlloc(&p, pool, 1); if (res == ResOK) { error("Error: Unexpectedly succeeded in" "allocating block from PoolN\n"); diff --git a/mps/code/poolsnc.c b/mps/code/poolsnc.c index ef295fc7d9a..e1758fe2f51 100644 --- a/mps/code/poolsnc.c +++ b/mps/code/poolsnc.c @@ -1,7 +1,7 @@ /* poolsnc.c: STACK NO CHECKING POOL CLASS * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. * * DESIGN * @@ -43,10 +43,18 @@ typedef struct SNCStruct { /* Forward declarations */ -extern SegClass SNCSegClassGet(void); -extern BufferClass SNCBufClassGet(void); +typedef SNC SNCPool; +#define SNCPoolCheck SNCCheck +DECLARE_CLASS(Pool, SNCPool, AbstractSegBufPool); + +DECLARE_CLASS(Seg, SNCSeg, MutatorSeg); +DECLARE_CLASS(Buffer, SNCBuf, RankBuf); static Bool SNCCheck(SNC snc); static void sncPopPartialSegChain(SNC snc, Buffer buf, Seg upTo); +static void sncSegBufferEmpty(Seg seg, Buffer buffer); +static Res sncSegScan(Bool *totalReturn, Seg seg, ScanState ss); +static void sncSegWalk(Seg seg, Format format, FormattedObjectsVisitor f, + void *p, size_t s); /* Management of segment chains @@ -74,20 +82,13 @@ typedef struct SNCBufStruct { } SNCBufStruct; -/* BufferSNCBuf -- convert generic Buffer to an SNCBuf */ - -#define BufferSNCBuf(buffer) ((SNCBuf)(buffer)) - - /* SNCBufCheck -- check consistency of an SNCBuf */ ATTRIBUTE_UNUSED static Bool SNCBufCheck(SNCBuf sncbuf) { - SegBuf segbuf; - + SegBuf segbuf = MustBeA(SegBuf, sncbuf); CHECKS(SNCBuf, sncbuf); - segbuf = &sncbuf->segBufStruct; CHECKD(SegBuf, segbuf); if (sncbuf->topseg != NULL) { CHECKD(Seg, sncbuf->topseg); @@ -100,10 +101,7 @@ static Bool SNCBufCheck(SNCBuf sncbuf) static Seg sncBufferTopSeg(Buffer buffer) { - SNCBuf sncbuf; - AVERT(Buffer, buffer); - sncbuf = BufferSNCBuf(buffer); - AVERT(SNCBuf, sncbuf); + SNCBuf sncbuf = MustBeA(SNCBuf, buffer); return sncbuf->topseg; } @@ -112,85 +110,69 @@ static Seg sncBufferTopSeg(Buffer buffer) static void sncBufferSetTopSeg(Buffer buffer, Seg seg) { - SNCBuf sncbuf; - AVERT(Buffer, buffer); + SNCBuf sncbuf = MustBeA(SNCBuf, buffer); if (NULL != seg) AVERT(Seg, seg); - sncbuf = BufferSNCBuf(buffer); - AVERT(SNCBuf, sncbuf); sncbuf->topseg = seg; } /* SNCBufInit -- Initialize an SNCBuf */ -static Res SNCBufInit(Buffer buffer, Pool pool, ArgList args) +static Res SNCBufInit(Buffer buffer, Pool pool, Bool isMutator, ArgList args) { SNCBuf sncbuf; Res res; - BufferClass superclass; - - AVERT(Buffer, buffer); - AVERT(Pool, pool); /* call next method */ - superclass = BUFFER_SUPERCLASS(SNCBufClass); - res = (*superclass->init)(buffer, pool, args); + res = NextMethod(Buffer, SNCBuf, init)(buffer, pool, isMutator, args); if (res != ResOK) return res; + sncbuf = CouldBeA(SNCBuf, buffer); - sncbuf = BufferSNCBuf(buffer); sncbuf->topseg = NULL; - sncbuf->sig = SNCBufSig; - AVERT(SNCBuf, sncbuf); + SetClassOfPoly(buffer, CLASS(SNCBuf)); + sncbuf->sig = SNCBufSig; + AVERC(SNCBuf, sncbuf); + return ResOK; } /* SNCBufFinish -- Finish an SNCBuf */ -static void SNCBufFinish(Buffer buffer) +static void SNCBufFinish(Inst inst) { - BufferClass super; - SNCBuf sncbuf; - SNC snc; - Pool pool; + Buffer buffer = MustBeA(Buffer, inst); + SNCBuf sncbuf = MustBeA(SNCBuf, buffer); + SNC snc = MustBeA(SNCPool, BufferPool(buffer)); - AVERT(Buffer, buffer); - sncbuf = BufferSNCBuf(buffer); - AVERT(SNCBuf, sncbuf); - pool = BufferPool(buffer); - - snc = PoolSNC(pool); - /* Put any segments which haven't bee popped onto the free list */ + /* Put any segments which haven't been popped onto the free list */ sncPopPartialSegChain(snc, buffer, NULL); sncbuf->sig = SigInvalid; - /* finish the superclass fields last */ - super = BUFFER_SUPERCLASS(SNCBufClass); - super->finish(buffer); + NextMethod(Inst, SNCBuf, finish)(inst); } /* SNCBufClass -- The class definition */ -DEFINE_BUFFER_CLASS(SNCBufClass, class) +DEFINE_CLASS(Buffer, SNCBuf, klass) { - INHERIT_CLASS(class, RankBufClass); - class->name = "SNCBUF"; - class->size = sizeof(SNCBufStruct); - class->init = SNCBufInit; - class->finish = SNCBufFinish; - AVERT(BufferClass, class); + INHERIT_CLASS(klass, SNCBuf, RankBuf); + klass->instClassStruct.finish = SNCBufFinish; + klass->size = sizeof(SNCBufStruct); + klass->init = SNCBufInit; + AVERT(BufferClass, klass); } /* SNCSegStruct -- SNC segment subclass * - * This subclass of GCSeg links segments in chains. + * This subclass of MutatorSeg links segments in chains. */ #define SNCSegSig ((Sig)0x51954C59) /* SIGSNCSeG */ @@ -206,9 +188,7 @@ typedef struct SNCSegStruct { #define SegSNCSeg(seg) ((SNCSeg)(seg)) #define SNCSegSeg(sncseg) ((Seg)(sncseg)) -#define sncSegNext(seg) \ - (SNCSegSeg(SegSNCSeg(seg)->next)) - +#define sncSegNext(seg) RVALUE(SNCSegSeg(SegSNCSeg(seg)->next)) #define sncSegSetNext(seg, nextseg) \ ((void)(SegSNCSeg(seg)->next = SegSNCSeg(nextseg))) @@ -226,42 +206,57 @@ static Bool SNCSegCheck(SNCSeg sncseg) /* sncSegInit -- Init method for SNC segments */ -static Res sncSegInit(Seg seg, Pool pool, Addr base, Size size, - Bool reservoirPermit, ArgList args) +static Res sncSegInit(Seg seg, Pool pool, Addr base, Size size, ArgList args) { - SegClass super; SNCSeg sncseg; Res res; - AVERT(Seg, seg); - sncseg = SegSNCSeg(seg); - AVERT(Pool, pool); - /* no useful checks for base and size */ - AVERT(Bool, reservoirPermit); - /* Initialize the superclass fields first via next-method call */ - super = SEG_SUPERCLASS(SNCSegClass); - res = super->init(seg, pool, base, size, reservoirPermit, args); + res = NextMethod(Seg, SNCSeg, init)(seg, pool, base, size, args); if (res != ResOK) return res; + sncseg = CouldBeA(SNCSeg, seg); + + AVERT(Pool, pool); + /* no useful checks for base and size */ sncseg->next = NULL; + + SetClassOfPoly(seg, CLASS(SNCSeg)); sncseg->sig = SNCSegSig; - AVERT(SNCSeg, sncseg); + AVERC(SNCSeg, sncseg); + return ResOK; } +/* sncSegFinish -- finish an SNC segment */ + +static void sncSegFinish(Inst inst) +{ + Seg seg = MustBeA(Seg, inst); + SNCSeg sncseg = MustBeA(SNCSeg, seg); + + sncseg->sig = SigInvalid; + + /* finish the superclass fields last */ + NextMethod(Inst, SNCSeg, finish)(inst); +} + + /* SNCSegClass -- Class definition for SNC segments */ -DEFINE_SEG_CLASS(SNCSegClass, class) +DEFINE_CLASS(Seg, SNCSeg, klass) { - INHERIT_CLASS(class, GCSegClass); - SegClassMixInNoSplitMerge(class); /* no support for this (yet) */ - class->name = "SNCSEG"; - class->size = sizeof(SNCSegStruct); - class->init = sncSegInit; - AVERT(SegClass, class); + INHERIT_CLASS(klass, SNCSeg, MutatorSeg); + SegClassMixInNoSplitMerge(klass); /* no support for this (yet) */ + klass->instClassStruct.finish = sncSegFinish; + klass->size = sizeof(SNCSegStruct); + klass->init = sncSegInit; + klass->bufferEmpty = sncSegBufferEmpty; + klass->scan = sncSegScan; + klass->walk = sncSegWalk; + AVERT(SegClass, klass); } @@ -280,7 +275,7 @@ static void sncRecordAllocatedSeg(Buffer buffer, Seg seg) /* sncRecordFreeSeg - stores a segment on the freelist */ -static void sncRecordFreeSeg(SNC snc, Seg seg) +static void sncRecordFreeSeg(Arena arena, SNC snc, Seg seg) { AVERT(SNC, snc); AVERT(Seg, seg); @@ -291,6 +286,11 @@ static void sncRecordFreeSeg(SNC snc, Seg seg) SegSetGrey(seg, TraceSetEMPTY); SegSetRankAndSummary(seg, RankSetEMPTY, RefSetEMPTY); + /* Pad the whole segment so we don't try to walk it. */ + ShieldExpose(arena, seg); + (*SNCPool(snc)->format->pad)(SegBase(seg), SegSize(seg)); + ShieldCover(arena, seg); + sncSegSetNext(seg, snc->freeSegs); snc->freeSegs = seg; } @@ -317,7 +317,7 @@ static void sncPopPartialSegChain(SNC snc, Buffer buf, Seg upTo) AVER(free != NULL); next = sncSegNext(free); sncSegSetNext(free, NULL); - sncRecordFreeSeg(snc, free); + sncRecordFreeSeg(BufferArena(buf), snc, free); free = next; } /* Make upTo the head of the buffer chain */ @@ -372,41 +372,50 @@ static void SNCVarargs(ArgStruct args[MPS_ARGS_MAX], va_list varargs) /* SNCInit -- initialize an SNC pool */ -static Res SNCInit(Pool pool, ArgList args) +static Res SNCInit(Pool pool, Arena arena, PoolClass klass, ArgList args) { SNC snc; - Format format; - ArgStruct arg; + Res res; - /* weak check, as half-way through initialization */ AVER(pool != NULL); + AVERT(Arena, arena); + AVERT(ArgList, args); + UNUSED(klass); /* used for debug pools only */ - snc = PoolSNC(pool); + res = NextMethod(Pool, SNCPool, init)(pool, arena, klass, args); + if (res != ResOK) + goto failNextInit; + snc = CouldBeA(SNCPool, pool); - ArgRequire(&arg, args, MPS_KEY_FORMAT); - format = arg.val.format; + /* Ensure a format was supplied in the argument list. */ + AVER(pool->format != NULL); - AVERT(Format, format); - AVER(FormatArena(format) == PoolArena(pool)); - pool->format = format; + pool->alignment = pool->format->alignment; + pool->alignShift = SizeLog2(pool->alignment); snc->freeSegs = NULL; - snc->sig = SNCSig; - AVERT(SNC, snc); - EVENT2(PoolInitSNC, pool, format); + SetClassOfPoly(pool, CLASS(SNCPool)); + snc->sig = SNCSig; + AVERC(SNCPool, snc); + + EVENT2(PoolInitSNC, pool, pool->format); + return ResOK; + +failNextInit: + AVER(res != ResOK); + return res; } /* SNCFinish -- finish an SNC pool */ -static void SNCFinish(Pool pool) +static void SNCFinish(Inst inst) { - SNC snc; + Pool pool = MustBeA(AbstractPool, inst); + SNC snc = MustBeA(SNCPool, pool); Ring ring, node, nextNode; - AVERT(Pool, pool); - snc = PoolSNC(pool); AVERT(SNC, snc); ring = &pool->segRing; @@ -415,12 +424,13 @@ static void SNCFinish(Pool pool) AVERT(Seg, seg); SegFree(seg); } + + NextMethod(Inst, SNCPool, finish)(inst); } static Res SNCBufferFill(Addr *baseReturn, Addr *limitReturn, - Pool pool, Buffer buffer, Size size, - Bool withReservoirPermit) + Pool pool, Buffer buffer, Size size) { SNC snc; Arena arena; @@ -433,7 +443,6 @@ static Res SNCBufferFill(Addr *baseReturn, Addr *limitReturn, AVERT(Pool, pool); AVERT(Buffer, buffer); AVER(size > 0); - AVERT(Bool, withReservoirPermit); AVER(BufferIsReset(buffer)); snc = PoolSNC(pool); @@ -447,8 +456,8 @@ static Res SNCBufferFill(Addr *baseReturn, Addr *limitReturn, /* No free seg, so create a new one */ arena = PoolArena(pool); asize = SizeArenaGrains(size, arena); - res = SegAlloc(&seg, SNCSegClassGet(), LocusPrefDefault(), - asize, pool, withReservoirPermit, argsNone); + res = SegAlloc(&seg, CLASS(SNCSeg), LocusPrefDefault(), + asize, pool, argsNone); if (res != ResOK) return res; @@ -462,70 +471,54 @@ static Res SNCBufferFill(Addr *baseReturn, Addr *limitReturn, AVERT(Seg, seg); /* put the segment on the buffer chain */ sncRecordAllocatedSeg(buffer, seg); - /* Permit the use of lightweight frames - .lw-frame-state */ - BufferFrameSetState(buffer, BufferFrameVALID); *baseReturn = SegBase(seg); *limitReturn = SegLimit(seg); return ResOK; } -static void SNCBufferEmpty(Pool pool, Buffer buffer, - Addr init, Addr limit) +static void sncSegBufferEmpty(Seg seg, Buffer buffer) { - SNC snc; - Seg seg; Arena arena; - Size size; + Pool pool; + Addr base, init, limit; - AVERT(Pool, pool); + AVERT(Seg, seg); AVERT(Buffer, buffer); - seg = BufferSeg(buffer); + base = BufferBase(buffer); + init = BufferGetInit(buffer); + limit = BufferLimit(buffer); + AVER(SegBase(seg) <= base); + AVER(base <= init); AVER(init <= limit); - AVER(SegLimit(seg) == limit); - snc = PoolSNC(pool); - AVERT(SNC, snc); - AVER(BufferFrameState(buffer) == BufferFrameVALID); - /* .lw-frame-state */ - BufferFrameSetState(buffer, BufferFrameDISABLED); + AVER(limit <= SegLimit(seg)); - arena = BufferArena(buffer); + pool = SegPool(seg); + arena = PoolArena(pool); - /* Pad the end unused space at the end of the segment */ - size = AddrOffset(init, limit); - if (size > 0) { + /* Pad the unused space at the end of the segment */ + if (init < limit) { ShieldExpose(arena, seg); - (*pool->format->pad)(init, size); + (*pool->format->pad)(init, AddrOffset(init, limit)); ShieldCover(arena, seg); } } -static Res SNCScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg) +static Res sncSegScan(Bool *totalReturn, Seg seg, ScanState ss) { Addr base, limit; Format format; - SNC snc; Res res; AVER(totalReturn != NULL); AVERT(ScanState, ss); AVERT(Seg, seg); - AVERT(Pool, pool); - snc = PoolSNC(pool); - AVERT(SNC, snc); - format = pool->format; + format = SegPool(seg)->format; base = SegBase(seg); - - /* If the segment is buffered, only walk as far as the end */ - /* of the initialized objects. */ - if (SegBuffer(seg) != NULL) { - limit = BufferScanLimit(SegBuffer(seg)); - } else { - limit = SegLimit(seg); - } - + limit = SegBufferScanLimit(seg); + if (base < limit) { res = FormatScan(format, ss, base, limit); if (res != ResOK) { @@ -544,43 +537,36 @@ static Res SNCScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg) static Res SNCFramePush(AllocFrame *frameReturn, Pool pool, Buffer buf) { - FrameState state; AVER(frameReturn != NULL); AVERT(Pool, pool); AVERT(Buffer, buf); - state = BufferFrameState(buf); - /* Sould have been notified of pending pops before this */ - AVER(state == BufferFrameVALID || state == BufferFrameDISABLED); - if (state == BufferFrameDISABLED) { - AVER(BufferIsReset(buf)); /* The buffer must be reset */ + if (BufferIsReset(buf)) { AVER(sncBufferTopSeg(buf) == NULL); /* The stack must be empty */ /* Use NULL to indicate an empty stack. .lw-frame-null */ *frameReturn = NULL; + } else if (BufferGetInit(buf) < SegLimit(BufferSeg(buf))) { + /* Frame pointer is limit of initialized objects in buffer. */ + *frameReturn = (AllocFrame)BufferGetInit(buf); } else { - /* Use the scan limit as the lightweight frame pointer */ - *frameReturn = (AllocFrame)BufferScanLimit(buf); + /* Can't use the limit of initialized objects as the frame pointer + * because it's not in the segment (see job003882). Instead, refill + * the buffer and put the frame pointer at the beginning. */ + Res res; + Addr base, limit; + BufferDetach(buf, pool); + res = SNCBufferFill(&base, &limit, pool, buf, PoolAlignment(pool)); + if (res != ResOK) + return res; + BufferAttach(buf, base, limit, base, 0); + AVER(BufferGetInit(buf) < SegLimit(BufferSeg(buf))); + *frameReturn = (AllocFrame)BufferGetInit(buf); } return ResOK; } - static Res SNCFramePop(Pool pool, Buffer buf, AllocFrame frame) -{ - AVERT(Pool, pool); - AVERT(Buffer, buf); - /* Normally the Pop would be handled as a lightweight pop */ - /* The only reason that might not happen is if the stack is empty */ - AVER(sncBufferTopSeg(buf) == NULL); - /* The only valid frame must also be NULL - .lw-frame-null */ - AVER(frame == NULL); - /* Popping an empty frame is a NOOP */ - return ResOK; -} - - -static void SNCFramePopPending(Pool pool, Buffer buf, AllocFrame frame) { Addr addr; SNC snc; @@ -589,8 +575,6 @@ static void SNCFramePopPending(Pool pool, Buffer buf, AllocFrame frame) /* frame is an Addr and can't be directly checked */ snc = PoolSNC(pool); AVERT(SNC, snc); - - AVER(BufferFrameState(buf) == BufferFrameVALID); if (frame == NULL) { /* corresponds to a pop to bottom of stack. .lw-frame-null */ @@ -601,13 +585,15 @@ static void SNCFramePopPending(Pool pool, Buffer buf, AllocFrame frame) Arena arena; Seg seg = NULL; /* suppress "may be used uninitialized" */ Bool foundSeg; + Buffer segBuf; arena = PoolArena(pool); addr = (Addr)frame; foundSeg = SegOfAddr(&seg, arena, addr); - AVER(foundSeg); + AVER(foundSeg); /* */ + AVER(SegPool(seg) == pool); - if (SegBuffer(seg) == buf) { + if (SegBuffer(&segBuf, seg) && segBuf == buf) { /* don't need to change the segment - just the alloc pointers */ AVER(addr <= BufferScanLimit(buf)); /* check direction of pop */ BufferSetAllocAddr(buf, addr); @@ -616,18 +602,18 @@ static void SNCFramePopPending(Pool pool, Buffer buf, AllocFrame frame) BufferDetach(buf, pool); sncPopPartialSegChain(snc, buf, seg); BufferAttach(buf, SegBase(seg), SegLimit(seg), addr, (Size)0); - /* Permit the use of lightweight frames - .lw-frame-state */ - BufferFrameSetState(buf, BufferFrameVALID); } } + + return ResOK; } -static void SNCWalk(Pool pool, Seg seg, FormattedObjectsVisitor f, - void *p, size_t s) +static void sncSegWalk(Seg seg, Format format, FormattedObjectsVisitor f, + void *p, size_t s) { - AVERT(Pool, pool); AVERT(Seg, seg); + AVERT(Format, format); AVER(FUNCHECK(f)); /* p and s are arbitrary closures and can't be checked */ @@ -637,19 +623,9 @@ static void SNCWalk(Pool pool, Seg seg, FormattedObjectsVisitor f, Addr object = SegBase(seg); Addr nextObject; Addr limit; - SNC snc; - Format format; + Pool pool = SegPool(seg); - snc = PoolSNC(pool); - AVERT(SNC, snc); - format = pool->format; - - /* If the segment is buffered, only walk as far as the end */ - /* of the initialized objects. Cf. SNCScan. */ - if (SegBuffer(seg) != NULL) - limit = BufferScanLimit(SegBuffer(seg)); - else - limit = SegLimit(seg); + limit = SegBufferScanLimit(seg); while(object < limit) { (*f)(object, format, pool, p, s); @@ -662,33 +638,74 @@ static void SNCWalk(Pool pool, Seg seg, FormattedObjectsVisitor f, } +/* SNCTotalSize -- total memory allocated from the arena */ + +static Size SNCTotalSize(Pool pool) +{ + SNC snc; + Ring ring, node, nextNode; + Size total = 0; + + AVERT(Pool, pool); + snc = PoolSNC(pool); + AVERT(SNC, snc); + + ring = &pool->segRing; + RING_FOR(node, ring, nextNode) { + Seg seg = SegOfPoolRing(node); + AVERT(Seg, seg); + total += SegSize(seg); + } + + return total; +} + + +/* SNCFreeSize -- free memory (unused by client program) */ + +static Size SNCFreeSize(Pool pool) +{ + SNC snc; + Seg seg; + Size free = 0; + + AVERT(Pool, pool); + snc = PoolSNC(pool); + AVERT(SNC, snc); + + seg = snc->freeSegs; + while (seg != NULL) { + AVERT(Seg, seg); + free += SegSize(seg); + seg = sncSegNext(seg); + } + + return free; +} + + /* SNCPoolClass -- the class definition */ -DEFINE_POOL_CLASS(SNCPoolClass, this) +DEFINE_CLASS(Pool, SNCPool, klass) { - INHERIT_CLASS(this, AbstractScanPoolClass); - PoolClassMixInFormat(this); - this->name = "SNC"; - this->size = sizeof(SNCStruct); - this->offset = offsetof(SNCStruct, poolStruct); - this->varargs = SNCVarargs; - this->init = SNCInit; - this->finish = SNCFinish; - this->bufferFill = SNCBufferFill; - this->bufferEmpty = SNCBufferEmpty; - this->scan = SNCScan; - this->framePush = SNCFramePush; - this->framePop = SNCFramePop; - this->framePopPending = SNCFramePopPending; - this->walk = SNCWalk; - this->bufferClass = SNCBufClassGet; - AVERT(PoolClass, this); + INHERIT_CLASS(klass, SNCPool, AbstractSegBufPool); + klass->instClassStruct.finish = SNCFinish; + klass->size = sizeof(SNCStruct); + klass->varargs = SNCVarargs; + klass->init = SNCInit; + klass->bufferFill = SNCBufferFill; + klass->framePush = SNCFramePush; + klass->framePop = SNCFramePop; + klass->bufferClass = SNCBufClassGet; + klass->totalSize = SNCTotalSize; + klass->freeSize = SNCFreeSize; + AVERT(PoolClass, klass); } mps_pool_class_t mps_class_snc(void) { - return (mps_pool_class_t)SNCPoolClassGet(); + return (mps_pool_class_t)CLASS(SNCPool); } @@ -698,8 +715,8 @@ ATTRIBUTE_UNUSED static Bool SNCCheck(SNC snc) { CHECKS(SNC, snc); + CHECKC(SNCPool, snc); CHECKD(Pool, SNCPool(snc)); - CHECKL(SNCPool(snc)->class == SNCPoolClassGet()); if (snc->freeSegs != NULL) { CHECKD(Seg, snc->freeSegs); } @@ -709,7 +726,7 @@ static Bool SNCCheck(SNC snc) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/thw3.h b/mps/code/prmc.h similarity index 64% rename from mps/code/thw3.h rename to mps/code/prmc.h index b19bfccadba..ce134476bfe 100644 --- a/mps/code/thw3.h +++ b/mps/code/prmc.h @@ -1,42 +1,44 @@ -/* thw3.h: WIN32 THREAD MANAGER HEADER +/* prmc.h: MUTATOR CONTEXT INTERFACE * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2016 Ravenbrook Limited. See end of file for license. * - * This is used in and and + * See for the design of the generic interface including + * the contracts for these functions. * - * .nt: uses Win32 specific stuff - * HANDLE - * DWORD + * This interface has several different implementations, typically one + * per platform, see for the various implementations. */ -#ifndef thw3_h -#define thw3_h +#ifndef prmc_h +#define prmc_h -#include "mpm.h" +#include "mpmtypes.h" -#if !defined(MPS_OS_W3) /* .nt */ -#error "Compiling thw3 when MPS_OS_W3 not defined." -#endif +#define MutatorContextSig ((Sig)0x519302C0) /* SIGnature MUTator COntext */ -#include "mpswin.h" +enum { + MutatorContextFAULT, /* Context of thread stopped by protection fault. */ + MutatorContextTHREAD, /* Context of thread stopped by thread manager. */ + MutatorContextLIMIT +}; -typedef struct mps_thr_s { /* Win32 thread structure */ - Sig sig; /* */ - 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 */ -} ThreadStruct; +typedef unsigned MutatorContextVar; + +extern Bool MutatorContextCheck(MutatorContext context); +extern Bool MutatorContextCanStepInstruction(MutatorContext context); +extern Res MutatorContextStepInstruction(MutatorContext context); +extern Addr MutatorContextSP(MutatorContext context); +extern Res MutatorContextScan(ScanState ss, MutatorContext context, + mps_area_scan_t scan, void *closure); + + +#endif /* prmc_h */ -#endif /* thw3_h */ /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/prmcan.c b/mps/code/prmcan.c index 0551744bfd1..92de55e64b6 100644 --- a/mps/code/prmcan.c +++ b/mps/code/prmcan.c @@ -1,15 +1,11 @@ -/* prmcan.c: PROTECTION MUTATOR CONTEXT (ANSI) +/* prmcan.c: MUTATOR CONTEXT (GENERIC OPERATING SYSTEM) * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 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 - * functions. - * - * .purpose: This module implements the part of the protection module - * that implements the MutatorFaultContext type. In this ANSI version - * none of the functions have a useful implementation. + * .purpose: Implement the mutator context module. See . + * In this version, for a generic operating system, none of the + * functions have a useful implementation. */ #include "mpm.h" @@ -17,29 +13,17 @@ SRCID(prmcan, "$Id$"); -/* ProtCanStepInstruction -- can the current instruction be single-stepped */ - -Bool ProtCanStepInstruction(MutatorFaultContext context) +Bool MutatorContextCheck(MutatorContext context) { UNUSED(context); - return FALSE; -} - - -/* ProtStepInstruction -- step over instruction by modifying context */ - -Res ProtStepInstruction(MutatorFaultContext context) -{ - UNUSED(context); - - return ResUNIMPL; + return TRUE; } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/eventrep.h b/mps/code/prmcanan.c similarity index 77% rename from mps/code/eventrep.h rename to mps/code/prmcanan.c index 0c66bc3e0fe..49d1c921669 100644 --- a/mps/code/eventrep.h +++ b/mps/code/prmcanan.c @@ -1,32 +1,37 @@ -/* eventrep.h: Allocation replayer interface - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. +/* prmcanan.c: MUTATOR CONTEXT (GENERIC PROCESSOR ARCHITECTURE) * * $Id$ + * Copyright (c) 2016 Ravenbrook Limited. See end of file for license. + * + * .purpose: Implement the mutator context module. See . + * In this version for a generic processor architecture, none of the + * functions have a useful implementation. */ -#ifndef eventrep_h -#define eventrep_h +#include "mpm.h" -#include "config.h" -/* override variety setting for EVENT */ -#define EVENT - -#include "eventcom.h" -#include "mpmtypes.h" +SRCID(prmcanan, "$Id$"); -extern Res EventRepInit(Bool partial); -extern void EventRepFinish(void); +Bool MutatorContextCanStepInstruction(MutatorContext context) +{ + UNUSED(context); -extern void EventReplay(Event event, Word etime); + return FALSE; +} -#endif /* eventrep_h */ +Res MutatorContextStepInstruction(MutatorContext context) +{ + UNUSED(context); + + return ResUNIMPL; +} /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/ssan.c b/mps/code/prmcfri3.c similarity index 71% rename from mps/code/ssan.c rename to mps/code/prmcfri3.c index fc3c0ec84df..f05988f273f 100644 --- a/mps/code/ssan.c +++ b/mps/code/prmcfri3.c @@ -1,43 +1,42 @@ -/* ssan.c: ANSI STACK SCANNER +/* prmcfri3.c: MUTATOR CONTEXT INTEL 386 (FREEBSD) * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. * - * 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. + * .purpose: Implement the mutator context module. See . + * + * + * SOURCES + * + * .source.i486: Intel486 Microprocessor Family Programmer's + * Reference Manual + * + * + * ASSUMPTIONS + * + * .sp: The stack pointer in the context is ESP. */ -#include +#include "prmcix.h" +#include "prmci3.h" -#include "mpmtypes.h" -#include "misc.h" -#include "ss.h" +SRCID(prmcfri3, "$Id$"); + +#if !defined(MPS_OS_FR) || !defined(MPS_ARCH_I3) +#error "prmcfri3.c is specific to MPS_OS_FR and MPS_ARCH_I3" +#endif -SRCID(ssan, "$Id$"); - - -Res StackScan(ScanState ss, Word *stackCold, - mps_area_scan_t scan_area, - void *closure) +Addr MutatorContextSP(MutatorContext context) { - jmp_buf jb; - Word *stackHot = (void *)&jb; - - (void)setjmp(jb); - - return StackScanInner(ss, stackCold, stackHot, sizeof jb / sizeof(Word), - scan_area, closure); + AVERT(MutatorContext, context); + return (Addr)context->ucontext->uc_mcontext.mc_esp; /* .sp */ } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/mpscmv.h b/mps/code/prmcfri6.c similarity index 77% rename from mps/code/mpscmv.h rename to mps/code/prmcfri6.c index 871ab7d4f63..586eec1647a 100644 --- a/mps/code/mpscmv.h +++ b/mps/code/prmcfri6.c @@ -1,26 +1,36 @@ -/* mpscmv.h: MEMORY POOL SYSTEM CLASS "MV" +/* prmcfri6.c: MUTATOR CONTEXT x64 (FREEBSD) * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. + * + * .purpose: Implement the mutator context module. See . + * + * + * ASSUMPTIONS + * + * .sp: The stack pointer in the context is RSP. */ -#ifndef mpscmv_h -#define mpscmv_h +#include "prmcix.h" +#include "prmci6.h" -#include "mps.h" +SRCID(prmcfri6, "$Id$"); -#define mps_mv_free_size mps_pool_free_size -#define mps_mv_size mps_pool_total_size +#if !defined(MPS_OS_FR) || !defined(MPS_ARCH_I6) +#error "prmcfri6.c is specific to MPS_OS_FR and MPS_ARCH_I6" +#endif -extern mps_pool_class_t mps_class_mv(void); -extern mps_pool_class_t mps_class_mv_debug(void); -#endif /* mpscmv_h */ +Addr MutatorContextSP(MutatorContext context) +{ + AVERT(MutatorContext, context); + return (Addr)context->ucontext->uc_mcontext.mc_rsp; /* .sp */ +} /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/proti3.c b/mps/code/prmci3.c similarity index 90% rename from mps/code/proti3.c rename to mps/code/prmci3.c index 76f7f7965f1..22190251a1f 100644 --- a/mps/code/proti3.c +++ b/mps/code/prmci3.c @@ -1,14 +1,13 @@ -/* proti3.c: PROTECTION MUTATOR CONTEXT (INTEL 386) +/* prmci3.c: MUTATOR CONTEXT (INTEL 386) * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. * - * .design: See for the generic design of the interface + * .design: See for the generic design of the interface * which is implemented in this module, including the contracts for the * functions. * - * .purpose: This module implements the part of the protection module - * that implements the MutatorFaultContext type. + * .purpose: Implement the mutator context module. See . * * .requirements: Current requirements are for limited support only, for * stepping the sorts of instructions that the Dylan compiler might @@ -31,9 +30,9 @@ * * ASSUMPTIONS * - * .assume.null: It's always safe for Prot*StepInstruction to return - * ResUNIMPL. A null implementation of this module would be overly - * conservative but otherwise correct. + * .assume.null: It's always safe for MutatorContextCanStepInstruction + * to return FALSE. A null implementation of this module would be + * overly conservative but otherwise correct. * * .assume.want: The Dylan implementation is likely to access a * weak table vector using either MOV r/m32,r32 or MOV r32,r/m32 @@ -52,10 +51,10 @@ #include "mpm.h" #include "prmci3.h" -SRCID(proti3, "$Id$"); +SRCID(prmci3, "$Id$"); #if !defined(MPS_ARCH_I3) -#error "proti3.c is specific to MPS_ARCH_I3" +#error "prmci3.c is specific to MPS_ARCH_I3" #endif @@ -100,7 +99,7 @@ static void DecodeModRM(unsigned int *modReturn, /* RegValue -- Return the value of a machine register from a context */ -static Word RegValue(MutatorFaultContext context, unsigned int regnum) +static Word RegValue(MutatorContext context, unsigned int regnum) { MRef addr; @@ -131,7 +130,7 @@ static Word SignedInsElt(Byte insvec[], Count i) static Bool DecodeSimpleMov(unsigned int *regnumReturn, MRef *memReturn, Size *inslenReturn, - MutatorFaultContext context, + MutatorContext context, Byte insvec[]) { unsigned int mod; @@ -178,7 +177,7 @@ static Bool DecodeSimpleMov(unsigned int *regnumReturn, static Bool IsSimpleMov(Size *inslenReturn, MRef *srcReturn, MRef *destReturn, - MutatorFaultContext context) + MutatorContext context) { Byte *insvec; unsigned int regnum; @@ -211,12 +210,14 @@ static Bool IsSimpleMov(Size *inslenReturn, } -Bool ProtCanStepInstruction(MutatorFaultContext context) +Bool MutatorContextCanStepInstruction(MutatorContext context) { Size inslen; MRef src; MRef dest; + AVERT(MutatorContext, context); + /* .assume.null */ /* .assume.want */ if(IsSimpleMov(&inslen, &src, &dest, context)) { @@ -227,12 +228,14 @@ Bool ProtCanStepInstruction(MutatorFaultContext context) } -Res ProtStepInstruction(MutatorFaultContext context) +Res MutatorContextStepInstruction(MutatorContext context) { Size inslen; MRef src; MRef dest; + AVERT(MutatorContext, context); + /* .assume.null */ /* .assume.want */ if(IsSimpleMov(&inslen, &src, &dest, context)) { @@ -247,7 +250,7 @@ Res ProtStepInstruction(MutatorFaultContext context) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/prmci3.h b/mps/code/prmci3.h index e3fbc4f866c..23f36303a0f 100644 --- a/mps/code/prmci3.h +++ b/mps/code/prmci3.h @@ -1,7 +1,7 @@ -/* prmci3.h: PROTECTION MUTATOR CONTEXT (Intel 386) +/* prmci3.h: MUTATOR CONTEXT (Intel 386) * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. * * .readership: MPS developers. */ @@ -14,18 +14,18 @@ typedef Word *MRef; /* pointer to a machine word */ -MRef Prmci3AddressHoldingReg(MutatorFaultContext, unsigned int); +MRef Prmci3AddressHoldingReg(MutatorContext, unsigned int); -void Prmci3DecodeFaultContext(MRef *, Byte **, MutatorFaultContext); +void Prmci3DecodeFaultContext(MRef *, Byte **, MutatorContext); -void Prmci3StepOverIns(MutatorFaultContext, Size); +void Prmci3StepOverIns(MutatorContext, Size); #endif /* prmci3_h */ /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/proti6.c b/mps/code/prmci6.c similarity index 80% rename from mps/code/proti6.c rename to mps/code/prmci6.c index a681c51e63f..f1df3aec90b 100644 --- a/mps/code/proti6.c +++ b/mps/code/prmci6.c @@ -1,14 +1,13 @@ -/* proti6.c: PROTECTION MUTATOR CONTEXT (x64) +/* prmci6.c: MUTATOR CONTEXT (x64) * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. * - * .design: See for the generic design of the interface + * .design: See for the generic design of the interface * which is implemented in this module, including the contracts for the * functions. * - * .purpose: This module implements the part of the protection module - * that implements the MutatorFaultContext type. + * .purpose: Implement the mutator context module. See . * * * SOURCES @@ -20,26 +19,26 @@ * * ASSUMPTIONS * - * .assume.null: It's always safe for Prot*StepInstruction to return - * ResUNIMPL. A null implementation of this module would be overly - * conservative but otherwise correct. + * .assume.null: It's always safe for MutatorContextCanStepInstruction + * to return FALSE. A null implementation of this module would be + * overly conservative but otherwise correct. * */ #include "mpm.h" #include "prmci6.h" -SRCID(proti6, "$Id$"); +SRCID(prmci6, "$Id$"); #if !defined(MPS_ARCH_I6) -#error "proti6.c is specific to MPS_ARCH_I6" +#error "prmci6.c is specific to MPS_ARCH_I6" #endif static Bool IsSimpleMov(Size *inslenReturn, MRef *srcReturn, MRef *destReturn, - MutatorFaultContext context) + MutatorContext context) { Byte *insvec; MRef faultmem; @@ -54,12 +53,14 @@ static Bool IsSimpleMov(Size *inslenReturn, } -Bool ProtCanStepInstruction(MutatorFaultContext context) +Bool MutatorContextCanStepInstruction(MutatorContext context) { Size inslen; MRef src; MRef dest; + AVERT(MutatorContext, context); + /* .assume.null */ if(IsSimpleMov(&inslen, &src, &dest, context)) { return TRUE; @@ -69,12 +70,14 @@ Bool ProtCanStepInstruction(MutatorFaultContext context) } -Res ProtStepInstruction(MutatorFaultContext context) +Res MutatorContextStepInstruction(MutatorContext context) { Size inslen; MRef src; MRef dest; + AVERT(MutatorContext, context); + /* .assume.null */ if(IsSimpleMov(&inslen, &src, &dest, context)) { *dest = *src; @@ -88,7 +91,7 @@ Res ProtStepInstruction(MutatorFaultContext context) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/prmci6.h b/mps/code/prmci6.h index a5ed5c99b77..516661592d2 100644 --- a/mps/code/prmci6.h +++ b/mps/code/prmci6.h @@ -1,7 +1,7 @@ -/* prmci6.h: PROTECTION MUTATOR CONTEXT (x64) +/* prmci6.h: MUTATOR CONTEXT (x64) * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. * * .readership: MPS developers. */ @@ -14,18 +14,18 @@ typedef Word *MRef; /* pointer to a machine word */ -MRef Prmci6AddressHoldingReg(MutatorFaultContext, unsigned int); +MRef Prmci6AddressHoldingReg(MutatorContext, unsigned int); -void Prmci6DecodeFaultContext(MRef *, Byte **, MutatorFaultContext); +void Prmci6DecodeFaultContext(MRef *, Byte **, MutatorContext); -void Prmci6StepOverIns(MutatorFaultContext, Size); +void Prmci6StepOverIns(MutatorContext, Size); #endif /* prmci6_h */ /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/prmci3fr.c b/mps/code/prmcix.c similarity index 54% rename from mps/code/prmci3fr.c rename to mps/code/prmcix.c index 87ec5f436dc..3f8b0391361 100644 --- a/mps/code/prmci3fr.c +++ b/mps/code/prmcix.c @@ -1,66 +1,90 @@ -/* prmci3fr.c: PROTECTION MUTATOR CONTEXT INTEL 386 (FREEBSD) +/* prmcix.c: MUTATOR CONTEXT (POSIX) * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2016-2018 Ravenbrook Limited. See end of file for license. * - * .purpose: This module implements the part of the protection module - * that decodes the MutatorFaultContext. - * - * - * SOURCES - * - * .source.i486: Intel486 Microprocessor Family Programmer's - * Reference Manual + * .purpose: Implement the mutator context module. See . * * * ASSUMPTIONS * - * .sp: The stack pointer in the context is ESP. - * - * .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. + * .context.regroots: The root registers are assumed to be recorded in + * the context at pointer-aligned boundaries. */ -#include "prmcix.h" -#include "prmci3.h" +#include "mpm.h" -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" +#if !defined(MPS_OS_FR) && !defined(MPS_OS_LI) +#error "prmcix.c is specific to MPS_OS_FR or MPS_OS_LI" #endif +#include "prmcix.h" -Addr MutatorFaultContextSP(MutatorFaultContext mfc) +SRCID(prmcix, "$Id$"); + + +Bool MutatorContextCheck(MutatorContext context) { - return (Addr)mfc->ucontext->uc_mcontext.mc_esp; /* .sp */ + CHECKS(MutatorContext, context); + CHECKL(NONNEGATIVE(context->var)); + CHECKL(context->var < MutatorContextLIMIT); + CHECKL((context->var == MutatorContextTHREAD) == (context->info == NULL)); + CHECKL(context->ucontext != NULL); + return TRUE; } -Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext mfc, - mps_area_scan_t scan_area, - void *closure) +void MutatorContextInitFault(MutatorContext context, siginfo_t *info, + ucontext_t *ucontext) { + AVER(context != NULL); + AVER(info != NULL); + AVER(ucontext != NULL); + + context->var = MutatorContextFAULT; + context->info = info; + context->ucontext = ucontext; + context->sig = MutatorContextSig; + + AVERT(MutatorContext, context); +} + + +void MutatorContextInitThread(MutatorContext context, ucontext_t *ucontext) +{ + AVER(context != NULL); + AVER(ucontext != NULL); + + context->var = MutatorContextTHREAD; + context->info = NULL; + context->ucontext = ucontext; + context->sig = MutatorContextSig; + + AVERT(MutatorContext, context); +} + + +Res MutatorContextScan(ScanState ss, MutatorContext context, + mps_area_scan_t scan_area, void *closure) +{ + mcontext_t *mc; Res res; - /* This scans the root registers (.context.regroots). It also unnecessarily - scans the rest of the context. The optimisation to scan only relevant - parts would be machine dependent. */ - res = TraceScanArea( - ss, - (Word *)mfc->ucontext, - (Word *)((char *)mfc->ucontext + sizeof(*(mfc->ucontext))), - scan_area, closure - ); - + /* This scans the root registers (.context.regroots). It also + unnecessarily scans the rest of the context. The optimisation + to scan only relevant parts would be machine dependent. */ + mc = &context->ucontext->uc_mcontext; + res = TraceScanArea(ss, + (Word *)mc, + (Word *)((char *)mc + sizeof(*mc)), + scan_area, closure); return res; } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2016-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * @@ -98,3 +122,4 @@ Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext mfc, * (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/mps/code/prmcix.h b/mps/code/prmcix.h index 684be0d452e..2a126fb08e0 100644 --- a/mps/code/prmcix.h +++ b/mps/code/prmcix.h @@ -1,7 +1,7 @@ -/* prmcix.h: PROTECTION MUTATOR CONTEXT (UNIX) +/* prmcix.h: MUTATOR CONTEXT (UNIX) * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. * * .readership: MPS developers. */ @@ -14,18 +14,23 @@ #include /* siginfo_t -- see .feature.li in config.h */ #include /* ucontext_t */ -typedef struct MutatorFaultContextStruct { /* Protection fault context data */ - siginfo_t *info; +typedef struct MutatorContextStruct { + Sig sig; /* */ + MutatorContextVar var; /* Discriminator. */ + siginfo_t *info; /* Signal info, if stopped by protection + * fault; NULL if stopped by thread manager. */ ucontext_t *ucontext; -} MutatorFaultContextStruct; +} MutatorContextStruct; +extern void MutatorContextInitFault(MutatorContext context, siginfo_t *info, ucontext_t *ucontext); +extern void MutatorContextInitThread(MutatorContext context, ucontext_t *ucontext); #endif /* prmcix_h */ /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/prmci3li.c b/mps/code/prmclii3.c similarity index 69% rename from mps/code/prmci3li.c rename to mps/code/prmclii3.c index da04ac4810b..4f04d1b2a8b 100644 --- a/mps/code/prmci3li.c +++ b/mps/code/prmclii3.c @@ -1,10 +1,9 @@ -/* prmci3li.c: PROTECTION MUTATOR CONTEXT INTEL 386 (LINUX) +/* prmclii3.c: MUTATOR CONTEXT INTEL 386 (LINUX) * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. * - * .purpose: This module implements the part of the protection module - * that decodes the MutatorFaultContext. + * .purpose: Implement the mutator context module. See . * * * SOURCES @@ -19,9 +18,6 @@ * * .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 registers in the context can be modified by * storing into an MRef pointer. */ @@ -29,30 +25,29 @@ #include "prmcix.h" #include "prmci3.h" -SRCID(prmci3li, "$Id$"); +SRCID(prmclii3, "$Id$"); #if !defined(MPS_OS_LI) || !defined(MPS_ARCH_I3) -#error "prmci3li.c is specific to MPS_OS_LI and MPS_ARCH_I3" +#error "prmclii3.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 Prmci3AddressHoldingReg(MutatorContext context, unsigned int regnum) { MRef gregs; - AVER(mfc != NULL); + AVERT(MutatorContext, context); 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; + AVER(sizeof(void *) == sizeof(*context->ucontext->uc_mcontext.gregs)); + gregs = (void *)context->ucontext->uc_mcontext.gregs; /* .source.i486 */ /* .assume.regref */ @@ -79,50 +74,40 @@ MRef Prmci3AddressHoldingReg(MutatorFaultContext mfc, unsigned int regnum) void Prmci3DecodeFaultContext(MRef *faultmemReturn, Byte **insvecReturn, - MutatorFaultContext mfc) + MutatorContext context) { + AVER(faultmemReturn != NULL); + AVER(insvecReturn != NULL); + AVERT(MutatorContext, context); + AVER(context->var == MutatorContextFAULT); + /* .source.linux.kernel (linux/arch/i386/mm/fault.c). */ - *faultmemReturn = (MRef)mfc->info->si_addr; - *insvecReturn = (Byte*)mfc->ucontext->uc_mcontext.gregs[REG_EIP]; + *faultmemReturn = (MRef)context->info->si_addr; + *insvecReturn = (Byte*)context->ucontext->uc_mcontext.gregs[REG_EIP]; } /* Prmci3StepOverIns -- modify context to step over instruction */ -void Prmci3StepOverIns(MutatorFaultContext mfc, Size inslen) +void Prmci3StepOverIns(MutatorContext context, Size inslen) { - mfc->ucontext->uc_mcontext.gregs[REG_EIP] += (unsigned long)inslen; + AVERT(MutatorContext, context); + + context->ucontext->uc_mcontext.gregs[REG_EIP] += (unsigned long)inslen; } -Addr MutatorFaultContextSP(MutatorFaultContext mfc) +Addr MutatorContextSP(MutatorContext context) { - return (Addr)mfc->ucontext->uc_mcontext.gregs[REG_ESP]; -} + AVERT(MutatorContext, context); - -Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext mfc, - mps_area_scan_t scan_area, - void *closure) -{ - mcontext_t *mc; - Res res; - - /* This scans the root registers (.context.regroots). It also - unnecessarily scans the rest of the context. The optimisation - to scan only relevant parts would be machine dependent. */ - mc = &mfc->ucontext->uc_mcontext; - res = TraceScanArea(ss, - (Word *)mc, - (Word *)((char *)mc + sizeof(*mc)), - scan_area, closure); - return res; + return (Addr)context->ucontext->uc_mcontext.gregs[REG_ESP]; } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/prmci6li.c b/mps/code/prmclii6.c similarity index 70% rename from mps/code/prmci6li.c rename to mps/code/prmclii6.c index 67354c99414..69fbbb41375 100644 --- a/mps/code/prmci6li.c +++ b/mps/code/prmclii6.c @@ -1,10 +1,9 @@ -/* prmci6li.c: PROTECTION MUTATOR CONTEXT x64 (LINUX) +/* prmclii6.c: MUTATOR CONTEXT x64 (LINUX) * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. * - * .purpose: This module implements the part of the protection module - * that decodes the MutatorFaultContext. + * .purpose: Implement the mutator context module. See . * * * SOURCES @@ -16,9 +15,6 @@ * * .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 registers in the context can be modified by * storing into an MRef pointer. */ @@ -26,30 +22,29 @@ #include "prmcix.h" #include "prmci6.h" -SRCID(prmci6li, "$Id$"); +SRCID(prmclii6, "$Id$"); #if !defined(MPS_OS_LI) || !defined(MPS_ARCH_I6) -#error "prmci6li.c is specific to MPS_OS_LI and MPS_ARCH_I6" +#error "prmclii6.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) +MRef Prmci6AddressHoldingReg(MutatorContext context, unsigned int regnum) { MRef gregs; - AVER(mfc != NULL); + AVERT(MutatorContext, context); AVER(NONNEGATIVE(regnum)); AVER(regnum <= 15); - AVER(mfc->ucontext != NULL); /* 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; + AVER(sizeof(void *) == sizeof(*context->ucontext->uc_mcontext.gregs)); + gregs = (void *)context->ucontext->uc_mcontext.gregs; /* .assume.regref */ /* The register numbers (REG_RAX etc.) are defined in @@ -83,50 +78,40 @@ MRef Prmci6AddressHoldingReg(MutatorFaultContext mfc, unsigned int regnum) void Prmci6DecodeFaultContext(MRef *faultmemReturn, Byte **insvecReturn, - MutatorFaultContext mfc) + MutatorContext context) { + AVER(faultmemReturn != NULL); + AVER(insvecReturn != NULL); + AVERT(MutatorContext, context); + AVER(context->var == MutatorContextFAULT); + /* .source.linux.kernel (linux/arch/x86/mm/fault.c). */ - *faultmemReturn = (MRef)mfc->info->si_addr; - *insvecReturn = (Byte*)mfc->ucontext->uc_mcontext.gregs[REG_RIP]; + *faultmemReturn = (MRef)context->info->si_addr; + *insvecReturn = (Byte*)context->ucontext->uc_mcontext.gregs[REG_RIP]; } /* Prmci6StepOverIns -- modify context to step over instruction */ -void Prmci6StepOverIns(MutatorFaultContext mfc, Size inslen) +void Prmci6StepOverIns(MutatorContext context, Size inslen) { - mfc->ucontext->uc_mcontext.gregs[REG_RIP] += (Word)inslen; + AVERT(MutatorContext, context); + + context->ucontext->uc_mcontext.gregs[REG_RIP] += (Word)inslen; } -Addr MutatorFaultContextSP(MutatorFaultContext mfc) +Addr MutatorContextSP(MutatorContext context) { - return (Addr)mfc->ucontext->uc_mcontext.gregs[REG_RSP]; -} + AVERT(MutatorContext, context); - -Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext mfc, - mps_area_scan_t scan_area, - void *closure) -{ - mcontext_t *mc; - Res res; - - /* This scans the root registers (.context.regroots). It also - unnecessarily scans the rest of the context. The optimisation - to scan only relevant parts would be machine dependent. */ - mc = &mfc->ucontext->uc_mcontext; - res = TraceScanArea(ss, - (Word *)mc, - (Word *)((char *)mc + sizeof(*mc)), - scan_area, closure); - return res; + return (Addr)context->ucontext->uc_mcontext.gregs[REG_RSP]; } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/prmcw3.c b/mps/code/prmcw3.c new file mode 100644 index 00000000000..ea1ae7a9569 --- /dev/null +++ b/mps/code/prmcw3.c @@ -0,0 +1,137 @@ +/* prmcw3.c: MUTATOR CONTEXT FOR WIN32 + * + * $Id$ + * Copyright (c) 2016 Ravenbrook Limited. See end of file for license. + * + * .purpose: Implement the mutator context module. See . + * + * + * ASSUMPTIONS + * + * .context.regroots: The root registers are assumed to be recorded in + * the context at word-aligned boundaries. + * + * .context.flags: The ContextFlags field in the CONTEXT structure + * determines what is recorded by GetThreadContext. This must include: + * + * .context.sp: CONTEXT_CONTROL, so that the stack pointer (Esp on + * IA-32; Rsp on x86-64) is recorded. + * + * .context.regroots: CONTEXT_INTEGER, so that the root registers + * (Edi, Esi, Ebx, Edx, Ecx, Eax on IA-32; Rdi, Rsi, Rbx, Rbp, Rdx, + * Rcx, Rax, R8, ..., R15 on x86-64) are recorded. + * + * See the header WinNT.h for documentation of CONTEXT and + * ContextFlags. + */ + +#include "prmcw3.h" + +SRCID(prmcw3, "$Id$"); + +#if !defined(MPS_OS_W3) +#error "prmcw3.c is specific to MPS_OS_W3" +#endif + + +Bool MutatorContextCheck(MutatorContext context) +{ + CHECKS(MutatorContext, context); + CHECKL(NONNEGATIVE(context->var)); + CHECKL(context->var < MutatorContextLIMIT); + return TRUE; +} + + +Res MutatorContextInitThread(MutatorContext context, HANDLE thread) +{ + BOOL success; + + AVER(context != NULL); + + context->var = MutatorContextTHREAD; + /* This dumps the relevant registers into the context */ + /* .context.flags */ + context->the.context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; + success = GetThreadContext(thread, &context->the.context); + if (!success) + return ResFAIL; + context->sig = MutatorContextSig; + + AVERT(MutatorContext, context); + return ResOK; +} + + +void MutatorContextInitFault(MutatorContext context, + LPEXCEPTION_POINTERS ep) +{ + AVER(context != NULL); + AVER(ep != NULL); + + context->var = MutatorContextFAULT; + context->the.ep = ep; + context->sig = MutatorContextSig; + + AVERT(MutatorContext, context); +} + + +Res MutatorContextScan(ScanState ss, MutatorContext context, + mps_area_scan_t scan_area, void *closure) +{ + CONTEXT *cx; + Res res; + + AVERT(ScanState, ss); + AVERT(MutatorContext, context); + AVER(context->var == MutatorContextTHREAD); + + cx = &context->the.context; + res = TraceScanArea(ss, (Word *)cx, (Word *)((char *)cx + sizeof *cx), + scan_area, closure); /* .context.regroots */ + + return res; +} + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2001-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: + * + * 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/mps/code/prmcw3.h b/mps/code/prmcw3.h index b83bd6aba7b..95b48cb210a 100644 --- a/mps/code/prmcw3.h +++ b/mps/code/prmcw3.h @@ -1,7 +1,7 @@ -/* prmcw3.h: PROTECTION FOR WIN32 +/* prmcw3.h: MUTATOR CONTEXT FOR WIN32 * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. * * .readership: MPS developers. */ @@ -9,23 +9,27 @@ #ifndef prmcw3_h #define prmcw3_h - #include "mpm.h" - #include "mpswin.h" +typedef struct MutatorContextStruct { + Sig sig; /* */ + MutatorContextVar var; /* Union discriminator */ + union { + LPEXCEPTION_POINTERS ep; /* Windows Exception Pointers */ + CONTEXT context; /* Thread context */ + } the; +} MutatorContextStruct; -typedef struct MutatorFaultContextStruct { /* Protection fault context data */ - LPEXCEPTION_POINTERS ep; /* Windows Exception Pointers */ -} MutatorFaultContextStruct; - +extern Res MutatorContextInitThread(MutatorContext context, HANDLE thread); +extern void MutatorContextInitFault(MutatorContext context, LPEXCEPTION_POINTERS ep); #endif /* prmcw3_h */ /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/prmci3w3.c b/mps/code/prmcw3i3.c similarity index 71% rename from mps/code/prmci3w3.c rename to mps/code/prmcw3i3.c index 4f85f677a81..f107f863275 100644 --- a/mps/code/prmci3w3.c +++ b/mps/code/prmcw3i3.c @@ -1,12 +1,11 @@ -/* prmci3w3.c: PROTECTION MUTATOR CONTEXT INTEL 386 (Windows) +/* prmcw3i3.c: MUTATOR CONTEXT INTEL 386 (Windows) * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. * * PURPOSE * - * .purpose: This module implements the part of the protection module - * that decodes the MutatorFaultContext. + * .purpose: Implement the mutator context module. See . * * SOURCES * @@ -17,29 +16,35 @@ * * .assume.regref: The registers in the context can be modified by * storing into an MRef pointer. + * + * .assume.sp: The stack pointer is stored in CONTEXT.Esp. This + * requires CONTEXT_CONTROL to be set in ContextFlags when + * GetThreadContext is called (see ). */ #include "prmcw3.h" #include "prmci3.h" #include "mpm.h" -SRCID(prmci3w3, "$Id$"); +SRCID(prmcw3i3, "$Id$"); #if !defined(MPS_OS_W3) || !defined(MPS_ARCH_I3) -#error "prmci3w3.c is specific to MPS_OS_W3 and MPS_ARCH_I3" +#error "prmcw3i3.c is specific to MPS_OS_W3 and MPS_ARCH_I3" #endif /* Prmci3AddressHoldingReg -- Return an address for a given machine register */ -MRef Prmci3AddressHoldingReg(MutatorFaultContext context, unsigned int regnum) +MRef Prmci3AddressHoldingReg(MutatorContext context, unsigned int regnum) { PCONTEXT wincont; + AVERT(MutatorContext, context); + AVER(context->var == MutatorContextFAULT); AVER(NONNEGATIVE(regnum)); AVER(regnum <= 7); - wincont = context->ep->ContextRecord; + wincont = context->the.ep->ContextRecord; switch (regnum) { case 0: return (MRef)&wincont->Eax; @@ -60,32 +65,49 @@ MRef Prmci3AddressHoldingReg(MutatorFaultContext context, unsigned int regnum) /* Prmci3DecodeFaultContext -- decode fault context */ void Prmci3DecodeFaultContext(MRef *faultmemReturn, Byte **insvecReturn, - MutatorFaultContext context) + MutatorContext context) { LPEXCEPTION_RECORD er; - er = context->ep->ExceptionRecord; + AVER(faultmemReturn != NULL); + AVER(insvecReturn != NULL); + AVERT(MutatorContext, context); + AVER(context->var == MutatorContextFAULT); + + er = context->the.ep->ExceptionRecord; /* Assert that this is an access violation. The computation of */ - /* faultmem depends on this. */ + /* faultmemReturn depends on this. */ AVER(er->ExceptionCode == EXCEPTION_ACCESS_VIOLATION); *faultmemReturn = (MRef)er->ExceptionInformation[1]; - *insvecReturn = (Byte*)context->ep->ContextRecord->Eip; + *insvecReturn = (Byte*)context->the.ep->ContextRecord->Eip; } /* Prmci3StepOverIns -- skip an instruction by changing the context */ -void Prmci3StepOverIns(MutatorFaultContext context, Size inslen) +void Prmci3StepOverIns(MutatorContext context, Size inslen) { - context->ep->ContextRecord->Eip += (DWORD)inslen; + AVERT(MutatorContext, context); + AVER(context->var == MutatorContextFAULT); + + context->the.ep->ContextRecord->Eip += (DWORD)inslen; +} + + +Addr MutatorContextSP(MutatorContext context) +{ + AVERT(MutatorContext, context); + AVER(context->var == MutatorContextTHREAD); + + return (Addr)context->the.context.Esp; /* .assume.sp */ } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/prmci6w3.c b/mps/code/prmcw3i6.c similarity index 73% rename from mps/code/prmci6w3.c rename to mps/code/prmcw3i6.c index 9fa31ed3187..489e07278ad 100644 --- a/mps/code/prmci6w3.c +++ b/mps/code/prmcw3i6.c @@ -1,12 +1,11 @@ -/* prmci6w3.c: PROTECTION MUTATOR CONTEXT INTEL x64 (Windows) +/* prmcw3i6.c: MUTATOR CONTEXT INTEL x64 (Windows) * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. * * PURPOSE * - * .purpose: This module implements the part of the protection module - * that decodes the MutatorFaultContext. + * .purpose: Implement the mutator context module. See . * * SOURCES * @@ -15,29 +14,35 @@ * * .assume.regref: The registers in the context can be modified by * storing into an MRef pointer. + * + * .assume.sp: The stack pointer is stored in CONTEXT.Rsp. This + * requires CONTEXT_CONTROL to be set in ContextFlags when + * GetThreadContext is called. */ #include "prmcw3.h" #include "prmci6.h" #include "mpm.h" -SRCID(prmci6w3, "$Id$"); +SRCID(prmcw3i6, "$Id$"); #if !defined(MPS_OS_W3) || !defined(MPS_ARCH_I6) -#error "prmci6w3.c is specific to MPS_OS_W3 and MPS_ARCH_I6" +#error "prmcw3i6.c is specific to MPS_OS_W3 and MPS_ARCH_I6" #endif /* Prmci6AddressHoldingReg -- Return an address for a given machine register */ -MRef Prmci6AddressHoldingReg(MutatorFaultContext context, unsigned int regnum) +MRef Prmci6AddressHoldingReg(MutatorContext context, unsigned int regnum) { PCONTEXT wincont; + AVERT(MutatorContext, context); + AVER(context->var == MutatorContextFAULT); AVER(NONNEGATIVE(regnum)); AVER(regnum <= 16); - wincont = context->ep->ContextRecord; + wincont = context->the.ep->ContextRecord; switch (regnum) { case 0: return (MRef)&wincont->Rax; @@ -66,32 +71,49 @@ MRef Prmci6AddressHoldingReg(MutatorFaultContext context, unsigned int regnum) /* Prmci6DecodeFaultContext -- decode fault context */ void Prmci6DecodeFaultContext(MRef *faultmemReturn, Byte **insvecReturn, - MutatorFaultContext context) + MutatorContext context) { LPEXCEPTION_RECORD er; - er = context->ep->ExceptionRecord; + AVER(faultmemReturn != NULL); + AVER(insvecReturn != NULL); + AVERT(MutatorContext, context); + AVER(context->var == MutatorContextFAULT); + + er = context->the.ep->ExceptionRecord; /* Assert that this is an access violation. The computation of */ /* faultmem depends on this. */ AVER(er->ExceptionCode == EXCEPTION_ACCESS_VIOLATION); *faultmemReturn = (MRef)er->ExceptionInformation[1]; - *insvecReturn = (Byte*)context->ep->ContextRecord->Rip; + *insvecReturn = (Byte*)context->the.ep->ContextRecord->Rip; } /* Prmci6StepOverIns -- skip an instruction by changing the context */ -void Prmci6StepOverIns(MutatorFaultContext context, Size inslen) +void Prmci6StepOverIns(MutatorContext context, Size inslen) { - context->ep->ContextRecord->Rip += (DWORD64)inslen; + AVERT(MutatorContext, context); + AVER(context->var == MutatorContextFAULT); + + context->the.ep->ContextRecord->Rip += (DWORD64)inslen; +} + + +Addr MutatorContextSP(MutatorContext context) +{ + AVERT(MutatorContext, context); + AVER(context->var == MutatorContextTHREAD); + + return (Addr)context->the.context.Rsp; /* .assume.sp */ } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/prmcxc.c b/mps/code/prmcxc.c new file mode 100644 index 00000000000..61cc3c9ea6e --- /dev/null +++ b/mps/code/prmcxc.c @@ -0,0 +1,128 @@ +/* prmcxc.c: MUTATOR CONTEXT (macOS) + * + * $Id$ + * Copyright (c) 2016-2018 Ravenbrook Limited. See end of file for license. + * + * .purpose: Implement the mutator context module. See . + * + * + * ASSUMPTIONS + * + * .context.regroots: The root registers are assumed to be recorded in + * the context at pointer-aligned boundaries. + */ + +#include "prmcxc.h" + +SRCID(prmcxc, "$Id$"); + +#if !defined(MPS_OS_XC) +#error "prmcxc.c is specific to MPS_OS_XC" +#endif + + +Bool MutatorContextCheck(MutatorContext context) +{ + CHECKS(MutatorContext, context); + CHECKL(sizeof *context->threadState == sizeof(THREAD_STATE_S)); + CHECKL(NONNEGATIVE(context->var)); + CHECKL(context->var < MutatorContextLIMIT); + CHECKL((context->var == MutatorContextTHREAD) == (context->address == NULL)); + CHECKL(context->threadState != NULL); + return TRUE; +} + + +void MutatorContextInitFault(MutatorContext context, Addr address, + THREAD_STATE_S *threadState) +{ + AVER(context != NULL); + AVER(address != NULL); + AVER(threadState != NULL); + + context->var = MutatorContextFAULT; + context->address = address; + context->threadState = threadState; + context->sig = MutatorContextSig; + + AVERT(MutatorContext, context); +} + + +void MutatorContextInitThread(MutatorContext context, + THREAD_STATE_S *threadState) +{ + AVER(context != NULL); + AVER(threadState != NULL); + + context->var = MutatorContextTHREAD; + context->address = NULL; + context->threadState = threadState; + context->sig = MutatorContextSig; + + AVERT(MutatorContext, context); +} + + +Res MutatorContextScan(ScanState ss, MutatorContext context, + mps_area_scan_t scan_area, void *closure) +{ + THREAD_STATE_S *mc; + Res res; + + AVERT(ScanState, ss); + AVERT(MutatorContext, context); + AVER(context->var == MutatorContextTHREAD); + + /* This scans the root registers (.context.regroots). It also + unnecessarily scans the rest of the context. The optimisation + to scan only relevant parts would be architecture dependent. */ + mc = context->threadState; + res = TraceScanArea(ss, + (Word *)mc, + (Word *)((char *)mc + sizeof(*mc)), + scan_area, closure); + return res; +} + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2016-2018 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/mps/code/prmcxc.h b/mps/code/prmcxc.h index 52e0515b3b4..30930589acf 100644 --- a/mps/code/prmcxc.h +++ b/mps/code/prmcxc.h @@ -1,7 +1,7 @@ -/* prmcxc.h: PROTECTION MUTATOR CONTEXT FOR OS X MACH +/* prmcxc.h: MUTATOR CONTEXT (macOS) * * $Id$ - * Copyright (c) 2001-2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * * .readership: MPS developers. */ @@ -14,20 +14,25 @@ #include #include -typedef struct MutatorFaultContextStruct { /* Protection fault context data */ - Addr address; +typedef struct MutatorContextStruct { + Sig sig; /* */ + MutatorContextVar var; /* Discriminator. */ + Addr address; /* Fault address, if stopped by protection + * fault; NULL if stopped by thread manager. */ THREAD_STATE_S *threadState; /* FIXME: Might need to get the floats in case the compiler stashes intermediate values in them. */ -} MutatorFaultContextStruct; +} MutatorContextStruct; +extern void MutatorContextInitFault(MutatorContext context, Addr address, THREAD_STATE_S *threadState); +extern void MutatorContextInitThread(MutatorContext context, THREAD_STATE_S *threadState); #endif /* prmcxc_h */ /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2013 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/prmci3xc.c b/mps/code/prmcxci3.c similarity index 70% rename from mps/code/prmci3xc.c rename to mps/code/prmcxci3.c index 8eda902c244..7098ab8e765 100644 --- a/mps/code/prmci3xc.c +++ b/mps/code/prmcxci3.c @@ -1,10 +1,9 @@ -/* prmci3xc.c: PROTECTION MUTATOR CONTEXT INTEL 386 (MAC OS X) +/* prmcxci3.c: MUTATOR CONTEXT (macOS, IA-32) * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * - * .purpose: This module implements the part of the protection module - * that decodes the MutatorFaultContext. + * .purpose: Implement the mutator context module. See . * * * SOURCES @@ -12,14 +11,9 @@ * .source.i486: Intel486 Microprocessor Family Programmer's * Reference Manual * - * .source.linux.kernel: Linux kernel source files. - * * * ASSUMPTIONS * - * .context.regroots: The root regs are assumed to be recorded in the context - * at pointer-aligned boundaries. - * * .assume.regref: The registers in the context can be modified by * storing into an MRef pointer. */ @@ -27,24 +21,24 @@ #include "prmcxc.h" #include "prmci3.h" -SRCID(prmci3xc, "$Id$"); +SRCID(prmcxci3, "$Id$"); #if !defined(MPS_OS_XC) || !defined(MPS_ARCH_I3) -#error "prmci3xc.c is specific to MPS_OS_XC and MPS_ARCH_I3" +#error "prmcxci3.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) +MRef Prmci3AddressHoldingReg(MutatorContext context, unsigned int regnum) { THREAD_STATE_S *threadState; - AVER(mfc != NULL); + AVERT(MutatorContext, context); AVER(NONNEGATIVE(regnum)); AVER(regnum <= 7); - AVER(mfc->threadState != NULL); - threadState = mfc->threadState; + + threadState = context->threadState; /* .source.i486 */ /* .assume.regref */ @@ -75,49 +69,40 @@ MRef Prmci3AddressHoldingReg(MutatorFaultContext mfc, unsigned int regnum) void Prmci3DecodeFaultContext(MRef *faultmemReturn, Byte **insvecReturn, - MutatorFaultContext mfc) + MutatorContext context) { - *faultmemReturn = (MRef)mfc->address; - *insvecReturn = (Byte*)mfc->threadState->__eip; + AVER(faultmemReturn != NULL); + AVER(insvecReturn != NULL); + AVERT(MutatorContext, context); + AVER(context->var == MutatorContextFAULT); + + *faultmemReturn = (MRef)context->address; + *insvecReturn = (Byte*)context->threadState->__eip; } /* Prmci3StepOverIns -- modify context to step over instruction */ -void Prmci3StepOverIns(MutatorFaultContext mfc, Size inslen) +void Prmci3StepOverIns(MutatorContext context, Size inslen) { - mfc->threadState->__eip += (Word)inslen; + AVERT(MutatorContext, context); + AVER(0 < inslen); + + context->threadState->__eip += (Word)inslen; } -Addr MutatorFaultContextSP(MutatorFaultContext mfc) +Addr MutatorContextSP(MutatorContext context) { - return (Addr)mfc->threadState->__esp; -} + AVERT(MutatorContext, context); - -Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext mfc, - mps_area_scan_t scan_area, - void *closure) -{ - x86_thread_state32_t *mc; - Res res; - - /* This scans the root registers (.context.regroots). It also - unnecessarily scans the rest of the context. The optimisation - to scan only relevant parts would be machine dependent. */ - mc = mfc->threadState; - res = TraceScanArea(ss, - (Word *)mc, - (Word *)((char *)mc + sizeof(*mc)), - scan_area, closure); - return res; + return (Addr)context->threadState->__esp; } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/prmci6xc.c b/mps/code/prmcxci6.c similarity index 72% rename from mps/code/prmci6xc.c rename to mps/code/prmcxci6.c index ae8ef06f739..6a50e73df6c 100644 --- a/mps/code/prmci6xc.c +++ b/mps/code/prmcxci6.c @@ -1,10 +1,9 @@ -/* prmci6xc.c: PROTECTION MUTATOR CONTEXT x64 (OS X) +/* prmcxci6.c: MUTATOR CONTEXT (macOS, x86-64) * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * - * .purpose: This module implements the part of the protection module - * that decodes the MutatorFaultContext. + * .purpose: Implement the mutator context module. See . * * * SOURCES @@ -14,9 +13,6 @@ * * .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 registers in the context can be modified by * storing into an MRef pointer. */ @@ -24,24 +20,24 @@ #include "prmcxc.h" #include "prmci6.h" -SRCID(prmci6xc, "$Id$"); +SRCID(prmcxci6, "$Id$"); #if !defined(MPS_OS_XC) || !defined(MPS_ARCH_I6) -#error "prmci6xc.c is specific to MPS_OS_XC and MPS_ARCH_I6" +#error "prmcxci6.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) +MRef Prmci6AddressHoldingReg(MutatorContext context, unsigned int regnum) { THREAD_STATE_S *threadState; - AVER(mfc != NULL); + AVERT(MutatorContext, context); AVER(NONNEGATIVE(regnum)); AVER(regnum <= 15); - AVER(mfc->threadState != NULL); - threadState = mfc->threadState; + + threadState = context->threadState; /* .assume.regref */ /* The register numbers (REG_RAX etc.) are defined in @@ -78,49 +74,40 @@ MRef Prmci6AddressHoldingReg(MutatorFaultContext mfc, unsigned int regnum) void Prmci6DecodeFaultContext(MRef *faultmemReturn, Byte **insvecReturn, - MutatorFaultContext mfc) + MutatorContext context) { - *faultmemReturn = (MRef)mfc->address; - *insvecReturn = (Byte*)mfc->threadState->__rip; + AVER(faultmemReturn != NULL); + AVER(insvecReturn != NULL); + AVERT(MutatorContext, context); + AVER(context->var == MutatorContextFAULT); + + *faultmemReturn = (MRef)context->address; + *insvecReturn = (Byte*)context->threadState->__rip; } /* Prmci6StepOverIns -- modify context to step over instruction */ -void Prmci6StepOverIns(MutatorFaultContext mfc, Size inslen) +void Prmci6StepOverIns(MutatorContext context, Size inslen) { - mfc->threadState->__rip += (Word)inslen; + AVERT(MutatorContext, context); + AVER(0 < inslen); + + context->threadState->__rip += (Word)inslen; } -Addr MutatorFaultContextSP(MutatorFaultContext mfc) +Addr MutatorContextSP(MutatorContext context) { - return (Addr)mfc->threadState->__rsp; -} + AVERT(MutatorContext, context); - -Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext mfc, - mps_area_scan_t scan_area, - void *closure) -{ - x86_thread_state64_t *mc; - Res res; - - /* This scans the root registers (.context.regroots). It also - unnecessarily scans the rest of the context. The optimisation - to scan only relevant parts would be machine dependent. */ - mc = mfc->threadState; - res = TraceScanArea(ss, - (Word *)mc, - (Word *)((char *)mc + sizeof(*mc)), - scan_area, closure); - return res; + return (Addr)context->threadState->__rsp; } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/prot.h b/mps/code/prot.h index f3104cb27b7..4a6f6a23aa2 100644 --- a/mps/code/prot.h +++ b/mps/code/prot.h @@ -1,7 +1,7 @@ /* 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. + * $Id$ + * Copyright (c) 2014-2016 Ravenbrook Limited. See end of file for license. * * See for the design of the generic interface including * the contracts for these functions. @@ -25,22 +25,12 @@ 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, - mps_area_scan_t scan, - void *closure); - - #endif /* prot_h */ /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2014 Ravenbrook Limited . + * Copyright (C) 2014-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/protix.c b/mps/code/protix.c index a243b009491..53753ed29e6 100644 --- a/mps/code/protix.c +++ b/mps/code/protix.c @@ -1,14 +1,10 @@ /* protix.c: PROTECTION FOR UNIX * * $Id$ - * Copyright (c) 2001-2015 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * * Somewhat generic across different Unix systems. Shared between - * OS X, FreeBSD, and 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). + * macOS, FreeBSD, and Linux. * * * SOURCES @@ -23,7 +19,7 @@ * 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 + * very obsolete FreeBSD 2.2.x series), as did macOS, but both now implement * it correctly as void *. caddr_t is usually char *. * * .assume.write-only: More of an anti-assumption really. We @@ -39,15 +35,15 @@ */ #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" +#if !defined(MPS_OS_FR) && !defined(MPS_OS_LI) && !defined(MPS_OS_XC) +#error "protix.c is specific to MPS_OS_FR, MPS_OS_LI or MPS_OS_XC" #endif +#include "vm.h" + #include #include - #include #include @@ -123,7 +119,7 @@ Size ProtGranularity(void) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2015 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/protli.c b/mps/code/protli.c deleted file mode 100644 index e157fe9875f..00000000000 --- a/mps/code/protli.c +++ /dev/null @@ -1,180 +0,0 @@ -/* protli.c: PROTECTION FOR LINUX - * - * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. - * - * SOURCES - * - * .source.linux.kernel: Linux kernel source files. - */ - -#include "prmcix.h" - -#ifndef MPS_OS_LI -#error "protli.c is Linux-specific, but MPS_OS_LI is not set" -#endif - -#include -#include -#include -#include -#include - -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 */ -/* sigaction(3). See ProtSetup. */ - -static struct sigaction sigNext; - - -/* sigHandle -- protection signal handler - * - * This is the signal handler installed by ProtSetup to deal with - * protection faults. It is installed on the SIGSEGV signal. - * It decodes the protection fault details from the signal context - * and passes them to ArenaAccess, which attempts to handle the - * fault and remove its cause. If the fault is handled, then - * the handler returns and execution resumes. If it isn't handled, - * then sigHandle does its best to pass the signal on to the - * previously installed signal handler (sigNext). - * - * .sigh.context: We check si_code for being a memory access - * si_addr gives the fault address. See - * .source.linux.kernel (linux/arch/i386/mm/fault.c and - * linux/arch/x86/mm/fault.c). - * - * .sigh.addr: We assume that the OS decodes the address to something - * sensible - */ - -/* This is defined here to keep the sources closer to those in protsgix.c - * They can't be merged yet because protsgix doesn't pass the context to - * ArenaAccess */ - -#define PROT_SIGNAL SIGSEGV - -static void sigHandle(int sig, siginfo_t *info, void *context) /* .sigh.args */ -{ - int e; - /* sigset renamed to asigset due to clash with global on Darwin. */ - sigset_t asigset, oldset; - struct sigaction sa; - - AVER(sig == PROT_SIGNAL); - - if(info->si_code == SEGV_ACCERR) { /* .sigh.context */ - AccessSet mode; - Addr base; - ucontext_t *ucontext; - MutatorFaultContextStruct mfContext; - - ucontext = (ucontext_t *)context; - mfContext.ucontext = ucontext; - mfContext.info = info; - - /* on linux we used to be able to tell whether this was a read or a write */ - mode = AccessREAD | AccessWRITE; - - /* We assume that the access is for one word at the address. */ - base = (Addr)info->si_addr; /* .sigh.addr */ - /* limit = AddrAdd(base, (Size)sizeof(Addr)); */ - - /* Offer each protection structure the opportunity to handle the */ - /* exception. If it succeeds, then allow the mutator to continue. */ - - if(ArenaAccess(base, mode, &mfContext)) - return; - } - - /* The exception was not handled by any known protection structure, */ - /* so throw it to the previously installed handler. That handler won't */ - /* get an accurate context (the MPS would fail if it were the second in */ - /* line) but it's the best we can do. */ - - e = sigaction(PROT_SIGNAL, &sigNext, &sa); - AVER(e == 0); - sigemptyset(&asigset); - sigaddset(&asigset, PROT_SIGNAL); - e = sigprocmask(SIG_UNBLOCK, &asigset, &oldset); - AVER(e == 0); - kill(getpid(), PROT_SIGNAL); - e = sigprocmask(SIG_SETMASK, &oldset, NULL); - AVER(e == 0); - e = sigaction(PROT_SIGNAL, &sa, NULL); - AVER(e == 0); -} - - -/* ProtSetup -- global protection setup - * - * Under Linux, the global setup involves installing a signal handler - * on SIGSEGV to catch and handle page faults (see sigHandle). - * The previous handler is recorded so that it can be reached from - * sigHandle if it fails to handle the fault. - * - * NOTE: There are problems with this approach: - * 1. we can't honor the sa_flags for the previous handler, - * 2. what if this thread is suspended just after calling signal(3)? - * The sigNext variable will never be initialized! - */ - -void ProtSetup(void) -{ - struct sigaction sa; - int result; - - sa.sa_sigaction = sigHandle; - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_SIGINFO; - - result = sigaction(PROT_SIGNAL, &sa, &sigNext); - AVER(result == 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/mps/code/protocol.c b/mps/code/protocol.c index bcc8ae56863..afa03344247 100644 --- a/mps/code/protocol.c +++ b/mps/code/protocol.c @@ -1,133 +1,169 @@ +/* The class definition for the root of the hierarchy */ + /* pool.c: PROTOCOL IMPLEMENTATION * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. * - * DESIGN - * - * .design: See + * See design.mps.protocol. */ #include "mpm.h" +#include "protocol.h" SRCID(protocol, "$Id$"); -/* ProtocolClassCheck -- check a protocol class */ +/* The class definitions for the root of the hierarchy */ -Bool ProtocolClassCheck(ProtocolClass class) +static void InstClassInitInternal(InstClass klass); + +DEFINE_CLASS(Inst, Inst, klass) { - CHECKS(ProtocolClass, class); - CHECKU(ProtocolClass, class->superclass); - CHECKL(FUNCHECK(class->coerceInst)); - CHECKL(FUNCHECK(class->coerceClass)); - return TRUE; + InstClassInitInternal(klass); + klass->instStruct.klass = CLASS(InstClass); + AVERT(InstClass, klass); +} + +DEFINE_CLASS(Inst, InstClass, klass) +{ + /* Can't use INHERIT_CLASS(klass, InstClass, Inst) here because it + causes infinite regression, so we have to set this one up by + hand. */ + InstClassInitInternal(klass); + klass->superclass = &CLASS_STATIC(Inst); + klass->name = "InstClass"; + klass->level = ClassLevelInstClass; + klass->display[ClassLevelInstClass] = CLASS_ID(InstClass); + AVERT(InstClass, klass); +} + +static void InstClassInitInternal(InstClass klass) +{ + ClassLevel i; + + klass->name = "Inst"; + klass->superclass = NULL; + for (i = 0; i < ClassDEPTH; ++i) + klass->display[i] = NULL; + klass->level = 0; + klass->display[klass->level] = CLASS_ID(Inst); + + /* Generic methods */ + klass->describe = InstDescribe; + klass->finish = InstFinish; + klass->init = InstInit; + + /* We can't call CLASS(InstClass) here because it causes a loop back + to here, so we have to tie this knot specially. */ + klass->instStruct.klass = &CLASS_STATIC(InstClass); + + klass->sig = InstClassSig; + AVERT(InstClass, klass); } -/* ProtocolInstCheck -- check a protocol instance */ +/* InstClassCheck -- check a protocol class */ -Bool ProtocolInstCheck(ProtocolInst inst) +Bool InstClassCheck(InstClass klass) { - CHECKS(ProtocolInst, inst); - CHECKD(ProtocolClass, inst->class); - return TRUE; -} - - -/* ProtocolIsSubclass -- a predicate for testing subclass relationships - * - * A protocol class is always a subclass of itself. This is implemented - * via the coerceClass method provided by each class. - */ -Bool ProtocolIsSubclass(ProtocolClass sub, ProtocolClass super) -{ - ProtocolClass coerced; - - AVERT(ProtocolClass, sub); - AVERT(ProtocolClass, super); - - if (sub->coerceClass(&coerced, sub, super)) { - AVERT(ProtocolClass, coerced); - return TRUE; - } else { - return FALSE; + ClassLevel i; + CHECKS(InstClass, klass); + CHECKL(klass->name != NULL); + CHECKL(klass->level < ClassDEPTH); + for (i = 0; i <= klass->level; ++i) { + CHECKL(klass->display[i] != NULL); } -} - - -/* ProtocolCoerceClass -- the default method for coerceClass - * - * This default method must be inherited by any subclass - * which does not perform a multiple inheritance. - */ -static Bool ProtocolCoerceClass(ProtocolClass *coerceResult, - ProtocolClass proClass, - ProtocolClass super) -{ - ProtocolClass p = proClass; - ProtocolClass root = ProtocolClassGet(); - - AVERT(ProtocolClass, proClass); - AVERT(ProtocolClass, super); - AVERT(ProtocolClass, root); - - while (p != super) { - AVERT(ProtocolClass, p); - if (p == root) - return FALSE; - p = p->superclass; + for (i = klass->level + 1; i < ClassDEPTH; ++i) { + CHECKL(klass->display[i] == NULL); } - *coerceResult = proClass; + CHECKL(FUNCHECK(klass->describe)); + CHECKL(FUNCHECK(klass->finish)); + CHECKL(FUNCHECK(klass->init)); return TRUE; } -/* ProtocolCoerceInst -- the default method for coerceInst +/* InstInit -- initialize a protocol instance * - * This default method must be inherited by any subclass - * which does not perform a multiple inheritance. + * Initialisation makes the instance valid, so that it will pass + * InstCheck, and the instance can be specialized to be a member of a + * subclass. */ -static Bool ProtocolCoerceInst(ProtocolInst *coerceResult, - ProtocolInst proInst, - ProtocolClass super) + +void InstInit(Inst inst) { - ProtocolClass p = proInst->class; - ProtocolClass root = ProtocolClassGet(); + AVER(inst != NULL); + inst->klass = CLASS(Inst); + AVERC(Inst, inst); +} - AVERT(ProtocolInst, proInst); - AVERT(ProtocolClass, super); - AVERT(ProtocolClass, root); - while (p != super) { - AVERT(ProtocolClass, p); - if (p == root) - return FALSE; - p = p->superclass; - } - *coerceResult = proInst; +/* InstFinish -- finish a protocol instance + * + * Finishing makes the instance invalid, so that it will fail + * InstCheck and can't be used. + */ + +static InstClassStruct invalidClassStruct = { + /* .instStruct = */ {&invalidClassStruct}, + /* .sig = */ SigInvalid, + /* .name = */ "Invalid", + /* .superclass = */ &invalidClassStruct, + /* .level = */ 0, + /* .display = */ {(ClassId)&invalidClassStruct}, + /* .describe = */ NULL, + /* .finish = */ NULL, + /* .init = */ NULL, +}; + +void InstFinish(Inst inst) +{ + AVERC(Inst, inst); + inst->klass = &invalidClassStruct; +} + + +/* InstCheck -- check a protocol instance */ + +Bool InstCheck(Inst inst) +{ + CHECKD(InstClass, inst->klass); return TRUE; } -/* The class definition for the root of the hierarchy */ - -DEFINE_CLASS(ProtocolClass, theClass) +void ClassRegister(InstClass klass) { - theClass->sig = ProtocolClassSig; - theClass->superclass = theClass; - theClass->coerceInst = ProtocolCoerceInst; - theClass->coerceClass = ProtocolCoerceClass; - AVERT(ProtocolClass, theClass); + Word classId; + + /* label the pool class with its name */ + EventInit(); + classId = EventInternString(ClassName(klass)); + /* NOTE: this breaks */ + EventLabelAddr((Addr)klass, classId); } +Res InstDescribe(Inst inst, mps_lib_FILE *stream, Count depth) +{ + InstClass klass; + + if (!TESTC(Inst, inst)) + return ResPARAM; + if (stream == NULL) + return ResPARAM; + klass = ClassOfPoly(Inst, inst); + return WriteF(stream, depth, + "$S $P\n", (WriteFS)ClassName(klass), inst, + NULL); +} /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/protocol.h b/mps/code/protocol.h index 4c70738e8f8..f3cda2ef08d 100644 --- a/mps/code/protocol.h +++ b/mps/code/protocol.h @@ -1,7 +1,9 @@ /* protocol.h: PROTOCOL INHERITANCE DEFINITIONS * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. + * + * See design.mps.protocol. */ #ifndef protocol_h @@ -11,176 +13,318 @@ #include "mpmtypes.h" -/* Name derivation macros. These are not intended to be used */ -/* outside of this file */ +/* Identifier derivation macros. + * + * These turn the base identifier of a class (e.g. "Inst") into other + * identifiers (e.g. "InstClassStruct"). These are not intended to be + * used outside of this file. These macros implement + * design.mps.protocol.overview.naming and + * design.mps.impl.derived-names. + * + * INST_TYPE derives the type of an instance of the class, + * e.g. "Land", which will be a pointer to an INST_STRUCT. + * + * INST_STRUCT derives the type of a structure of an instance, + * e.g. "LandStruct". + * + * INST_CHECK derives the name of the checking function for the + * instance, e.g. "LandCheck". + * + * CLASS_TYPE derives the type of the class, e.g. "LandClass", which + * will be a pointer to a CLASS_STRUCT. + * + * CLASS_STRUCT derives the type of the structure of the class, + * e.g. "LandClassStruct". + * + * CLASS_ENSURE derives the name of the ensure function that returns + * the canonical class object, e.g. "LandClassGet". + * + * CLASS_INIT derives the name of the init function that initializes a + * CLASS_STRUCT, e.g. "LandClassInit". + * + * CLASS_CHECK derives the name of the checking function for the + * class, e.g. "LandClassCheck". + * + * CLASS_GUARDIAN derives the name of a boolean that indicates whether + * the canonical class object has been initialized yet, + * e.g. "ClassGuardianLand". + * + * CLASS_STATIC derives the name of a static global variable that + * contains the canonical class object, e.g. "ClassStaticLand". + * + * KIND_CLASS derives the class name of a kind, which is used in + * contexts like CLASS_TYPE(KIND_CLASS(kind)), so that the kind "Land" + * is implemented by the canonical "LandClassClass". + */ -#define DERIVE_LOCAL(name) protocol ## name -#define DERIVE_STRUCT(name) name ## Struct -#define DERIVE_ENSURE(name) name ## Get -#define DERIVE_ENSURE_OLD(name) Ensure ## name -#define DERIVE_ENSURE_INTERNAL(name) protocolGet ## name -#define DERIVE_GUARDIAN(name) protocol ## name ## Guardian -#define DERIVE_STATIC_STORAGE(name) protocol ## name ## Struct +#define INST_TYPE(klass) klass +#define INST_STRUCT(klass) klass ## Struct +#define INST_CHECK(klass) klass ## Check +#define CLASS_TYPE(klass) klass ## Class +#define CLASS_STRUCT(klass) klass ## ClassStruct +#define CLASS_ENSURE(klass) klass ## ClassGet +#define CLASS_INIT(klass) klass ## ClassInit +#define CLASS_CHECK(klass) klass ## ClassCheck +#define CLASS_GUARDIAN(klass) ClassGuardian ## klass +#define CLASS_STATIC(klass) ClassStatic ## klass +#define KIND_CLASS(klass) klass ## Class -/* Macro to set the superclass field. This is not intended */ -/* to be used outside this file. This is a polymorphic macro */ -/* named as a function. See */ +/* ClassId -- static identity of a class + * + * We use the address of the static storage for the canonical class + * object as the class id, suitable for fast comparison. This is not + * intended to be dereferenced. We would like to define it as a + * pointer to an incomplete structure, but GCC 4.7 buggily complains + * about punning if we do that, so use void *, even though that's a + * bit more error prone. + */ -#define ProtocolClassSetSuperclassPoly(class, super) \ - (((ProtocolClass)(class))->superclass) = (ProtocolClass)(super) +typedef void *ClassId; +#define CLASS_ID(klass) ((ClassId)&CLASS_STATIC(klass)) -/* DEFINE_CLASS -- the standard macro for defining a ProtocolClass */ +/* DECLARE_CLASS -- declare the existence of a protocol class + * + * Declares a prototype for the class ensure function, which ensures + * that the class is initialized once and return it. See + * design.mps.protocol.if.declare-class. + */ -#define DEFINE_CLASS(className, var) \ - static Bool DERIVE_GUARDIAN(className) = FALSE; \ - static DERIVE_STRUCT(className) DERIVE_STATIC_STORAGE(className); \ - static void DERIVE_ENSURE_INTERNAL(className)(className); \ - extern className DERIVE_ENSURE(className)(void); \ - className DERIVE_ENSURE(className)(void) \ +#define DECLARE_CLASS(kind, klass, super) \ + extern CLASS_TYPE(kind) CLASS_ENSURE(klass)(void); \ + extern void CLASS_INIT(klass)(CLASS_TYPE(kind) var); \ + extern CLASS_STRUCT(kind) CLASS_STATIC(klass); \ + enum { ClassLevel ## klass = ClassLevel ## super + 1 } + + +/* DEFINE_CLASS -- define a protocol class + * + * Defines the static storage and functions for the canonical class + * object for a class. Takes care to avoid initializing the class + * twice, even when called asynchronously from multiple threads, since + * this code can be reached without first entering an arena. See + * design.mps.protocol.if.define-class. + */ + +#define DEFINE_CLASS(kind, className, var) \ + static Bool CLASS_GUARDIAN(className) = FALSE; \ + CLASS_STRUCT(kind) CLASS_STATIC(className); \ + CLASS_TYPE(kind) CLASS_ENSURE(className)(void) \ { \ - if (DERIVE_GUARDIAN(className) == FALSE) { \ + CLASS_TYPE(kind) klass = &CLASS_STATIC(className); \ + if (CLASS_GUARDIAN(className) == FALSE) { \ LockClaimGlobalRecursive(); \ - if (DERIVE_GUARDIAN(className) == FALSE) { \ - DERIVE_ENSURE_INTERNAL(className) \ - (&DERIVE_STATIC_STORAGE(className)); \ - DERIVE_GUARDIAN(className) = TRUE; \ + if (CLASS_GUARDIAN(className) == FALSE) { \ + CLASS_INIT(className)(klass); \ + /* Prevent infinite regress. */ \ + if (CLASS_ID(className) != CLASS_ID(InstClass) && \ + CLASS_ID(className) != CLASS_ID(Inst)) \ + SetClassOfPoly(klass, CLASS(KIND_CLASS(kind))); \ + AVER(CLASS_CHECK(kind)(klass)); \ + CLASS_GUARDIAN(className) = TRUE; \ + ClassRegister(MustBeA(InstClass, klass)); \ } \ LockReleaseGlobalRecursive(); \ } \ - return &DERIVE_STATIC_STORAGE(className); \ + return klass; \ } \ - /* old name for backward compatibility */ \ - extern className DERIVE_ENSURE_OLD(className)(void); \ - className DERIVE_ENSURE_OLD(className)(void) \ - { \ - return DERIVE_ENSURE(className)(); \ - } \ - static void DERIVE_ENSURE_INTERNAL(className) (className var) + void CLASS_INIT(className)(CLASS_TYPE(kind) var) -/* INHERIT_CLASS -- the standard macro for inheriting from a superclass */ +/* CLASS -- expression for getting a class + * + * Use this to get a class, rather than calling anything defined by + * DEFINE_CLASS directly. See design.mps.protocol.if.class. + */ -#define INHERIT_CLASS(this, parentName) \ +#define CLASS(klass) (CLASS_ENSURE(klass)()) + + +/* INHERIT_CLASS -- inheriting from a superclass + * + * This macro is used at the start of a class definition to inherit + * the superclass and override the fields essential to the workings of + * the protocol. See design.mps.protocol.if.inheritance. + */ + +#define INHERIT_CLASS(this, _class, super) \ BEGIN \ - parentName DERIVE_LOCAL(parentName) = DERIVE_ENSURE(parentName)(); \ - *this = *(DERIVE_LOCAL(parentName)); \ - ProtocolClassSetSuperclassPoly(this, DERIVE_LOCAL(parentName)); \ + InstClass instClass = (InstClass)(this); \ + CLASS_INIT(super)(this); \ + instClass->superclass = (InstClass)CLASS(super); \ + instClass->name = #_class; \ + instClass->level = instClass->superclass->level + 1; \ + AVER(instClass->level < ClassDEPTH); \ + instClass->display[instClass->level] = CLASS_ID(_class); \ END -/* DEFINE_ALIAS_CLASS -- define a new class for the same type +/* Inst -- the base class of the hierarchy * - * A convenience macro. Aliases the structure and pointer types - * for className to be the same as typeName, and then defines - * the class className. + * An InstStruct named instStruct must be the first field of any + * instance structure using the protocol + * (design.mps.protocol.overview.prefix). */ -#define DEFINE_ALIAS_CLASS(className, typeName, var) \ - typedef typeName className; \ - typedef DERIVE_STRUCT(typeName) DERIVE_STRUCT(className); \ - DEFINE_CLASS(className, var) +typedef struct InstStruct *Inst; +typedef struct InstClassStruct *InstClass; +typedef struct InstStruct { + InstClass klass; + /* Do not add permanent fields here. Introduce a subclass. */ +} InstStruct; -#define ProtocolClassSig ((Sig)0x519B60C7) /* SIGnature PROtocol CLass */ -#define ProtocolInstSig ((Sig)0x519B6014) /* SIGnature PROtocol INst */ +typedef const char *ClassName; +typedef unsigned char ClassLevel; +typedef Res (*DescribeMethod)(Inst inst, mps_lib_FILE *stream, Count depth); +typedef void (*InstInitMethod)(Inst inst); +typedef void (*FinishMethod)(Inst inst); +#define ClassDEPTH 8 /* maximum depth of class hierarchy */ +#define InstClassSig ((Sig)0x519B1452) /* SIGnature Protocol INST */ -/* ProtocolClass -- the class containing the support for the protocol */ - -typedef struct ProtocolClassStruct *ProtocolClass; - - -/* ProtocolInst -- the instance structure for support of the protocol */ - -typedef struct ProtocolInstStruct *ProtocolInst; - - -/* ProtocolCoerceInstMethod -- coerce "pro" to an instance of "interface" - * - * If "pro" is an instance of "interface", then returns TRUE - * and sets coerceResult to point directly to the part of "pro" - * which contains the slots for "interface" - * RHSK 2006-04-05 s/interface/interfaceIn/: job000605, suspect msvc bug. - */ -typedef Bool (*ProtocolCoerceInstMethod)(ProtocolInst *coerceResult, - ProtocolInst pro, - ProtocolClass interfaceIn); - -/* ProtocolCoerceClassMethod -- coerce "proClass" to an "interface" class - * - * If "proClass" is a subclass of "interface", then returns TRUE - * and sets coerceResult to point directly to the part of - * "proClass" which contains the slots for "interface". - * RHSK 2006-04-05 s/interface/interfaceIn/: job000605, suspect msvc bug. - */ -typedef Bool (*ProtocolCoerceClassMethod)(ProtocolClass *coerceResult, - ProtocolClass proClass, - ProtocolClass interfaceIn); - - - -typedef struct ProtocolClassStruct { - Sig sig; /* */ - ProtocolClass superclass; /* the superclass */ - ProtocolCoerceInstMethod coerceInst; /* coerce instance to super */ - ProtocolCoerceClassMethod coerceClass; /* coerce class to superclass */ -} ProtocolClassStruct; - - -typedef struct ProtocolInstStruct { +typedef struct InstClassStruct { + InstStruct instStruct; /* classes are instances of kinds */ Sig sig; /* */ - ProtocolClass class; /* the class */ -} ProtocolInstStruct; + ClassName name; /* human readable name such as "Land" */ + InstClass superclass; /* pointer to direct superclass */ + ClassLevel level; /* distance from root of class hierarchy */ + ClassId display[ClassDEPTH]; /* classes at this level and above */ + DescribeMethod describe; /* write a debugging description */ + FinishMethod finish; /* finish instance */ + InstInitMethod init; /* base init method */ +} InstClassStruct; + +enum {ClassLevelNoSuper = -1}; +DECLARE_CLASS(Inst, Inst, NoSuper); +DECLARE_CLASS(Inst, InstClass, Inst); + +extern Bool InstClassCheck(InstClass klass); +extern Bool InstCheck(Inst inst); +extern void InstInit(Inst inst); +extern void InstFinish(Inst inst); +extern Res InstDescribe(Inst inst, mps_lib_FILE *stream, Count depth); -/* ProtocolClassGet -- Returns the root of the protocol class hierarchy +/* ClassRegister -- class registration * - * Function name conforms to standard conventions for - * protocols. + * This is called once for each class initialised by DEFINE_CLASS and + * is not intended for use outside this file. */ -extern ProtocolClass ProtocolClassGet(void); + +extern void ClassRegister(InstClass klass); -/* Checking functions */ - -extern Bool ProtocolClassCheck(ProtocolClass class); -extern Bool ProtocolInstCheck(ProtocolInst pro); - - -/* ProtocolIsSubclass - use macro IsSubclass to access this. +/* IsSubclass, IsA -- fast subclass test * - * A predicate for testing subclass relationships. - * A protocol class is always a subclass of itself. + * The InstClassStruct is arranged to make these tests fast and + * simple, so that it can be used as a consistency check in the MPS. + * See design.mps.protocol.impl.subclass. */ -extern Bool ProtocolIsSubclass(ProtocolClass sub, ProtocolClass super); + +#define IsSubclass(sub, super) \ + (((InstClass)(sub))->display[ClassLevel ## super] == CLASS_ID(super)) + +#define IsA(_class, inst) \ + IsSubclass(CouldBeA(Inst, inst)->klass, _class) + +#define IsNonNullAndA(_class, inst) \ + ((inst) != NULL && \ + CouldBeA(Inst, inst)->klass != NULL && \ + IsA(_class, inst)) -/* Protocol introspection interface */ - -/* The following are macros because of the need to cast */ -/* subtypes of ProtocolClass. Nevertheless they are named */ -/* as functions. See */ - - -#define ProtocolClassSuperclassPoly(class) \ - (((ProtocolClass)(class))->superclass) - -#define ClassOfPoly(inst) ((ProtocolInst)(inst)->class) - -#define IsSubclassPoly(sub, super) \ - ProtocolIsSubclass((ProtocolClass)(sub), (ProtocolClass)(super)) - - -/* SUPERCLASS - get the superclass object, given a class name +/* CouldBeA, MustBeA -- coerce instances * - * Returns the superclass, with type ProtocolClass. Clients will - * probably wish to cast this. See - * + * CouldBeA converts an instance to another class without checking, + * like C++ ``static_cast``. See design.mps.protocol.if.could-be-a. + * + * MustBeA converts an instance to another class, but checks that the + * object is a subclass, causing an assertion if not (depending on + * build variety). See design.mps.protocol.if.must-be-a. It is like + * C++ "dynamic_cast" with an assert. + * + * MustBeA_CRITICAL is like MustBeA for use on the critical path, + * where it does no checking at all in production builds. See + * design.mps.protocol.if.must-be-a.critical. */ -#define SUPERCLASS(className) \ - ProtocolClassSuperclassPoly(DERIVE_ENSURE(className)()) + +#define CouldBeA(klass, inst) ((INST_TYPE(klass))(inst)) + +#define MustBeA(_class, inst) \ + CouldBeA(_class, \ + AVERPC(IsNonNullAndA(_class, inst), \ + "MustBeA " #_class ": " #inst, \ + inst)) + +#define MustBeA_CRITICAL(_class, inst) \ + CouldBeA(_class, \ + AVERPC_CRITICAL(IsNonNullAndA(_class, inst), \ + "MustBeA " #_class ": " #inst, \ + inst)) + + +/* Protocol introspection interface + * + * The following are macros because of the need to cast subtypes of + * InstClass. Nevertheless they are named as functions. See + * design.mps.protocol.introspect. + */ + +#define SuperclassPoly(kind, klass) \ + MustBeA(KIND_CLASS(kind), MustBeA(InstClass, klass)->superclass) + +#define ClassOfPoly(kind, inst) \ + MustBeA(KIND_CLASS(kind), MustBeA(Inst, inst)->klass) + + +/* ClassName -- get the human readable name of a class + * + * ClassName is used in describe methods and other unsafe places, so + * we don't use MustBeA. + */ + +#define ClassName(klass) RVALUE(CouldBeA(InstClass, klass)->name) + + +/* SetClassOfPoly -- set the class of an object + * + * This should only be used when specialising an instance to be a + * member of a subclass, once the instance has been initialized. See + * design.mps.protocol.if.set-class-of-poly. + */ + +#define SetClassOfPoly(inst, _class) \ + BEGIN MustBeA(Inst, inst)->klass = MustBeA(InstClass, _class); END + + +/* Method -- method call + * + * Use this macro to call a method on a class, rather than accessing + * the class directly. See design.mps.protocol.if.method. For + * example: + * + * res = Method(Land, land, insert)(land, range); + */ + +#define Method(kind, inst, meth) (ClassOfPoly(kind, inst)->meth) + + +/* NextMethod -- call a method in the superclass + * + * See design.mps.protocol.int.static-superclass. + * + * TODO: All uses of NextMethod are statically known, but several + * experiments with statically generating some kind of SUPERCLASS + * lookup have failed because the names of types, classes, and the + * hierarchy are inconsistent. Revisit this later. + */ + +#define SUPERCLASS(kind, klass) \ + MustBeA(KIND_CLASS(kind), CouldBeA(InstClass, CLASS(klass))->superclass) + +#define NextMethod(kind, klass, meth) (SUPERCLASS(kind, klass)->meth) #endif /* protocol_h */ @@ -188,7 +332,7 @@ extern Bool ProtocolIsSubclass(ProtocolClass sub, ProtocolClass super); /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/protsgix.c b/mps/code/protsgix.c index f74a840f6bb..22f867d1c49 100644 --- a/mps/code/protsgix.c +++ b/mps/code/protsgix.c @@ -1,16 +1,18 @@ -/* protsgix.c: PROTECTION (SIGNAL HANDLER) FOR UNIX +/* protsgix.c: PROTECTION (SIGNAL HANDLER) FOR POSIX * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 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). + * It is designed to run on any POSIX-compliant Unix. * * * SOURCES * + * .source.posix: POSIX specifications for signal.h and sigaction + * + * + * * .source.man: sigaction(2): FreeBSD System Calls Manual. * * .source.merge: A blend from primarily the FreeBSD version (protfri3.c) @@ -20,12 +22,14 @@ #include "mpm.h" -#if !defined(MPS_OS_FR) -#error "protsgix.c is Unix-specific, currently for MPS_OS_FR" +#if !defined(MPS_OS_FR) && !defined(MPS_OS_LI) +#error "protsgix.c is specific to MPS_OS_FR or MPS_OS_LI" #endif +#include "prmcix.h" + #include /* for many functions */ -#include /* for getpid */ +#include /* for ucontext_t */ #include /* for getpid */ SRCID(protsgix, "$Id$"); @@ -36,66 +40,66 @@ SRCID(protsgix, "$Id$"); static struct sigaction sigNext; + /* sigHandle -- protection signal handler * - * This is the signal handler installed by ProtSetup to deal with - * protection faults. It is installed on the PROT_SIGNAL (a macro - * defined according to the platform in config.h) signal. It - * decodes the protection fault details from the signal context and - * passes them to ArenaAccess, which attempts to handle the fault and - * remove its cause. If the fault is handled, then the handler - * returns and execution resumes. If it isn't handled, then - * sigHandle does its best to pass the signal on to the previously - * installed signal handler (sigNext); which it does by signalling - * itself using kill(2). + * This is the signal handler installed by ProtSetup to deal with + * protection faults. It is installed on the signal given by the + * PROT_SIGNAL macro (that is, SIGSEGV). It constructs a mutator + * context based on the signal context, and passes it to ArenaAccess, + * which attempts to handle the fault and remove its cause. If the + * fault is handled, then the handler returns and execution resumes. + * If it isn't handled, then sigHandle does its best to pass the + * signal on to the previously installed signal handler (sigNext); + * which it does by signalling itself using kill(2). * - * .sigh.args: The sigaction manual page .source.man documents three - * different handler prototypes: ANSI C sa_handler, traditional BSD - * sa_handler, and POSIX SA_SIGINFO sa_sigaction. The ANSI C - * prototype isn't powerful enough for us (can't get addresses), and - * the manual page deprecates the BSD sa_handler in favour of the - * POSIX SA_SIGINFO sa_sigaction. In that prototype, the arguments - * are: signal number, pointer to signal info structure, pointer to - * signal context structure. + * .sigh.args: We set the SA_SIGINFO flag in the sa_flags field of the + * sigaction structure, and so the signal handler in the sa_sigaction + * field receives three arguments: signal number, pointer to signal + * info structure, pointer to signal context structure. * - * .sigh.context: We use the PROT_SIGINFO_GOOD macro to (usually) check - * the info->si_code. The macro is platform dependent and defined in - * config.h. We assume that info->si_addr is the fault address. This - * assumption turns out to fail for PowerPC Darwin (we use protxcpp.c - * there). + * .sigh.check: We check that info->si_code is SEGV_ACCERR (meaning + * "Invalid permissions for mapped object"). * - * .sigh.mode: The fault type (read/write) does not appear to be - * available to the signal handler (see mail archive). + * .sign.addr: If so, we assume info->si_addr is the fault address. + * + * .sigh.mode: The fault type (read/write) does not appear to be + * available to the signal handler (see mail archive). */ -static void sigHandle(int sig, siginfo_t *info, void *context) /* .sigh.args */ +#define PROT_SIGNAL SIGSEGV + +static void sigHandle(int sig, siginfo_t *info, void *uap) /* .sigh.args */ { int e; /* sigset renamed to asigset due to clash with global on Darwin. */ sigset_t asigset, oldset; struct sigaction sa; - - UNUSED(context); + AVER(sig == PROT_SIGNAL); - /* .sigh.context */ - if(PROT_SIGINFO_GOOD(info)) { + if(info->si_code == SEGV_ACCERR) { /* .sigh.check */ AccessSet mode; Addr base; + MutatorContextStruct context; + + MutatorContextInitFault(&context, info, (ucontext_t *)uap); mode = AccessREAD | AccessWRITE; /* .sigh.mode */ /* We assume that the access is for one word at the address. */ - base = (Addr)info->si_addr; /* .sigh.context */ + base = (Addr)info->si_addr; /* .sigh.addr */ /* Offer each protection structure the opportunity to handle the */ /* exception. If it succeeds, then allow the mutator to continue. */ - if(ArenaAccess(base, mode, NULL)) + if(ArenaAccess(base, mode, &context)) return; } /* The exception was not handled by any known protection structure, */ - /* so throw it to the previously installed handler. */ + /* so throw it to the previously installed handler. That handler won't */ + /* get an accurate context (the MPS would fail if it were the second in */ + /* line) but it's the best we can do. */ e = sigaction(PROT_SIGNAL, &sigNext, &sa); AVER(e == 0); @@ -113,15 +117,16 @@ static void sigHandle(int sig, siginfo_t *info, void *context) /* .sigh.args */ /* ProtSetup -- global protection setup * - * Under Unix, the global setup involves installing a signal - * handler on PROT_SIGNAL to catch and handle page faults (see - * sigHandle). The previous handler is recorded so that it can be - * reached from sigHandle if it fails to handle the fault. + * Under Unix, the global setup involves installing a signal handler + * on PROT_SIGNAL to catch and handle page faults (see sigHandle). + * The previous handler is recorded so that it can be reached from + * sigHandle if it fails to handle the fault. * * NOTE: There are problems with this approach: * 1. we can't honor the sa_flags for the previous handler, * 2. what if this thread is suspended just after calling signal(3)? - * The sigNext variable will never be initialized! */ + * The sigNext variable will never be initialized! + */ void ProtSetup(void) { @@ -139,7 +144,7 @@ void ProtSetup(void) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/protw3.c b/mps/code/protw3.c index a8dddd74fe2..a7878e7b7eb 100644 --- a/mps/code/protw3.c +++ b/mps/code/protw3.c @@ -1,20 +1,17 @@ /* protw3.c: PROTECTION FOR WIN32 * * $Id$ - * Copyright (c) 2001-2015 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 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" +#if !defined(MPS_OS_W3) +#error "protw3.c is specific to MPS_OS_W3" #endif #include "mpswin.h" +#include "vm.h" /* PageSize */ SRCID(protw3, "$Id$"); @@ -47,14 +44,14 @@ LONG WINAPI ProtSEHfilter(LPEXCEPTION_POINTERS info) AccessSet mode; Addr base, limit; LONG action; - MutatorFaultContextStruct context; + MutatorContextStruct context; er = info->ExceptionRecord; if(er->ExceptionCode != EXCEPTION_ACCESS_VIOLATION) return EXCEPTION_CONTINUE_SEARCH; - - context.ep = info; + + MutatorContextInitFault(&context, info); /* assert that the exception is continuable */ /* Note that Microsoft say that this field should be 0 or */ @@ -141,7 +138,7 @@ void ProtSync(Arena arena) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2015 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/protxc.c b/mps/code/protxc.c index 7e8f230d061..a02824ee2ef 100644 --- a/mps/code/protxc.c +++ b/mps/code/protxc.c @@ -1,9 +1,9 @@ -/* protxc.c: PROTECTION EXCEPTION HANDLER FOR OS X MACH +/* protxc.c: PROTECTION EXCEPTION HANDLER (macOS) * * $Id$ - * Copyright (c) 2013-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2013-2018 Ravenbrook Limited. See end of file for license. * - * This is the protection exception handling code for OS X using the + * This is the protection exception handling code for macOS using the * Mach interface (not pthreads). * * In Mach, a thread that hits protected memory is suspended, and a message @@ -15,7 +15,7 @@ * at the next level out (the levels are thread, task, host) by sending a * "fail" reply. * - * In OS X, pthreads are implemented by Mach threads. (The implementation is + * In macOS, pthreads are implemented by Mach threads. (The implementation is * part of the XNU source code at opensource.apple.com. [copy to import?]) So * we can use some pthread interfaces (pthread_create, pthread_once) for * convenience in setting up threads. @@ -47,7 +47,7 @@ * TRANSGRESSIONS * * .trans.stdlib: It's OK to use the C library from here because we know - * we're on OS X and not freestanding. In particular, we use memcpy. + * we're on macOS and not freestanding. In particular, we use memcpy. * * .trans.must: Various OS calls are asserted to succeed, since there isn't * really a dynamic reason they should fail, so it must be a static error. @@ -56,14 +56,14 @@ */ #include "mpm.h" + +#if !defined(MPS_OS_XC) +#error "protxc.c is specific to MPS_OS_XC" +#endif + #include "prmcxc.h" #include "protxc.h" -#include /* see .trans.stdlib */ -#include /* see .trans.stdlib */ - -#include - #include #include #include @@ -72,10 +72,9 @@ #include #include #include - -#if !defined(MPS_OS_XC) -#error "protxc.c is OS X specific" -#endif +#include +#include /* see .trans.stdlib */ +#include /* see .trans.stdlib */ SRCID(protxc, "$Id$"); @@ -178,7 +177,7 @@ static void protMustSend(mach_msg_header_t *head) /* protCatchOne -- catch one EXC_BAD_ACCESS exception message. * - * OS X provides a function exc_server (in + * macOS provides a function exc_server (in * /usr/lib/system/libsystem_kernel.dylib) that's documented in the XNU * sources * and generated by the Mach Interface Generator (mig). It unpacks @@ -234,20 +233,19 @@ static void protCatchOne(void) re-entered. */ if (request.code[0] == KERN_PROTECTION_FAILURE) { - MutatorFaultContextStruct mfcStruct; + MutatorContextStruct context; /* The cast via Word suppresses "cast to pointer from integer of 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 = (void *)request.old_state; + MutatorContextInitFault(&context, (Addr)(Word)request.code[1], + (void *)request.old_state); - if (ArenaAccess(mfcStruct.address, + if (ArenaAccess(context.address, AccessREAD | AccessWRITE, - &mfcStruct)) { + &context)) { /* Send a reply that will cause the thread to continue. Note that ArenaAccess may have updated request.old_state - via mfcStruct.thread_state, and that will get copied to the + via context.thread_state, and that will get copied to the reply and affect the state the thread resumes in. */ protBuildReply(&reply, &request, KERN_SUCCESS); protMustSend(&reply.Head); @@ -280,7 +278,8 @@ static void protCatchOne(void) */ ATTRIBUTE_NORETURN -static void *protCatchThread(void *p) { +static void *protCatchThread(void *p) +{ UNUSED(p); for (;;) protCatchOne(); @@ -289,34 +288,20 @@ static void *protCatchThread(void *p) { /* ProtThreadRegister -- register a thread for protection exception handling */ -extern void ProtThreadRegister(Bool setup) +void ProtThreadRegister(void) { kern_return_t kr; - mach_msg_type_number_t old_exception_count; + mach_msg_type_number_t old_exception_count = 1; exception_mask_t old_exception_masks; exception_behavior_t behavior; mach_port_t old_exception_ports; exception_behavior_t old_behaviors; thread_state_flavor_t old_flavors; mach_port_t self; - static mach_port_t setupThread = MACH_PORT_NULL; self = mach_thread_self(); AVER(MACH_PORT_VALID(self)); - - /* Avoid setting up the exception handler for the thread that calls - ProtSetup twice, in the case where the mutator registers that thread - explicitly. We need a special case because we don't require thread - registration of the sole thread of a single-threaded mutator. */ - if (setup) { - AVER(setupThread == MACH_PORT_NULL); - setupThread = self; - } else { - AVER(setupThread != MACH_PORT_NULL); - if (self == setupThread) - return; - } - + /* Ask to receive EXC_BAD_ACCESS exceptions on our port, complete with thread state and identity information in the message. The MACH_EXCEPTION_CODES flag causes the code fields to be @@ -338,18 +323,22 @@ extern void ProtThreadRegister(Bool setup) mach_error("ERROR: MPS thread_swap_exception_ports", kr); /* .trans.must */ AVER(old_exception_masks == EXC_MASK_BAD_ACCESS); AVER(old_exception_count == 1); - AVER(old_exception_ports == MACH_PORT_NULL); /* .assume.only-port */ + AVER(old_exception_ports == MACH_PORT_NULL + || old_exception_ports == protExcPort); /* .assume.only-port */ } -/* ProtSetup -- set up protection exception handling */ +/* protExcThreadStart -- create exception port, register the current + * thread with that port, and create a thread to handle exception + * messages. + */ -static void protSetupInner(void) +static void protExcThreadStart(void) { kern_return_t kr; - int pr; - pthread_t excThread; mach_port_t self; + pthread_t excThread; + int pr; /* Create a port to send and receive exceptions. */ self = mach_task_self(); @@ -373,18 +362,43 @@ static void protSetupInner(void) if (kr != KERN_SUCCESS) mach_error("ERROR: MPS mach_port_insert_right", kr); /* .trans.must */ - ProtThreadRegister(TRUE); + /* We don't require the mutator to register the sole thread in a + * single-threaded program, so register it automatically now. */ + ProtThreadRegister(); - /* Launch the exception handling thread. We use pthread_create because - it's much simpler than setting up a thread from scratch using Mach, - and that's basically what it does. See [Libc] - */ + /* Launch the exception handling thread. We use pthread_create + * because it's much simpler than setting up a thread from scratch + * using Mach, and that's basically what it does. See [Libc] + * */ pr = pthread_create(&excThread, NULL, protCatchThread, NULL); AVER(pr == 0); if (pr != 0) fprintf(stderr, "ERROR: MPS pthread_create: %d\n", pr); /* .trans.must */ } + +/* protAtForkChild -- support for fork() + * + */ + +static void protAtForkChild(void) +{ + /* Restart the exception handling thread + . */ + protExcThreadStart(); +} + + +/* ProtSetup -- set up protection exception handling */ + +static void protSetupInner(void) +{ + protExcThreadStart(); + + /* Install fork handlers . */ + pthread_atfork(NULL, NULL, protAtForkChild); +} + void ProtSetup(void) { int pr; @@ -401,7 +415,7 @@ void ProtSetup(void) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2013-2014 Ravenbrook Limited . + * Copyright (C) 2013-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/protxc.h b/mps/code/protxc.h index 26403b47cbc..5ccc8c9cbf6 100644 --- a/mps/code/protxc.h +++ b/mps/code/protxc.h @@ -1,19 +1,19 @@ -/* protxc.h: PROTECTION EXCPETION HANDLER FOR OS X MACH +/* protxc.h: PROTECTION EXCEPTION HANDLER (macOS) * * $Id$ - * Copyright (c) 2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2013-2018 Ravenbrook Limited. See end of file for license. */ #ifndef protxc_h #define protxc_h -extern void ProtThreadRegister(Bool setup); +extern void ProtThreadRegister(void); #endif /* protxc_h */ /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2013 Ravenbrook Limited . + * Copyright (C) 2013-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/pthrdext.c b/mps/code/pthrdext.c index 59d5899c326..176a5d51b8d 100644 --- a/mps/code/pthrdext.c +++ b/mps/code/pthrdext.c @@ -1,7 +1,7 @@ /* pthreadext.c: POSIX THREAD EXTENSIONS * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * * .purpose: Provides extension to Pthreads. * @@ -12,31 +12,25 @@ * (, ). */ - #include "mpm.h" -#include -#include -#include /* see .feature.li in config.h */ -#include -#include -#include -#include +#if !defined(MPS_OS_FR) && !defined(MPS_OS_LI) +#error "protsgix.c is specific to MPS_OS_FR or MPS_OS_LI" +#endif #include "pthrdext.h" +#include +#include +#include +#include +#include /* see .feature.li in config.h */ +#include +#include + SRCID(pthreadext, "$Id$"); -/* PTHREADEXT_SIGSUSPEND, PTHREADEXT_SIGRESUME -- signals used - * - * See - */ - -#define PTHREADEXT_SIGSUSPEND SIGXFSZ -#define PTHREADEXT_SIGRESUME SIGXCPU - - /* Static data initialized on first use of the module * See .* */ @@ -64,8 +58,6 @@ static RingStruct suspendedRing; /* PThreadext suspend ring */ * * See * - * The interface for determining the MFC might be platform specific. - * * Handle PTHREADEXT_SIGSUSPEND in the target thread, to suspend it until * receiving PTHREADEXT_SIGRESUME (resume). Note that this is run with both * PTHREADEXT_SIGSUSPEND and PTHREADEXT_SIGRESUME blocked. Having @@ -77,11 +69,11 @@ static RingStruct suspendedRing; /* PThreadext suspend ring */ static void suspendSignalHandler(int sig, siginfo_t *info, - void *context) + void *uap) { sigset_t signal_set; ucontext_t ucontext; - MutatorFaultContextStruct mfContext; + MutatorContextStruct context; AVER(sig == PTHREADEXT_SIGSUSPEND); UNUSED(sig); @@ -90,9 +82,9 @@ static void suspendSignalHandler(int sig, AVER(suspendingVictim != NULL); /* copy the ucontext structure so we definitely have it on our stack, * not (e.g.) shared with other threads. */ - ucontext = *(ucontext_t *)context; - mfContext.ucontext = &ucontext; - suspendingVictim->suspendedMFC = &mfContext; + ucontext = *(ucontext_t *)uap; + MutatorContextInitThread(&context, &ucontext); + suspendingVictim->context = &context; /* Block all signals except PTHREADEXT_SIGRESUME while suspended. */ sigfillset(&signal_set); sigdelset(&signal_set, PTHREADEXT_SIGRESUME); @@ -100,7 +92,6 @@ static void suspendSignalHandler(int sig, sigsuspend(&signal_set); /* Once here, the resume signal handler has run to completion. */ - return; } @@ -113,7 +104,6 @@ static void resumeSignalHandler(int sig) { AVER(sig == PTHREADEXT_SIGRESUME); UNUSED(sig); - return; } /* PThreadextModuleInit -- Initialize the PThreadext module @@ -169,7 +159,7 @@ static void PThreadextModuleInit(void) /* PThreadextCheck -- check the consistency of a PThreadext structure */ -extern Bool PThreadextCheck(PThreadext pthreadext) +Bool PThreadextCheck(PThreadext pthreadext) { int status; @@ -180,7 +170,7 @@ extern Bool PThreadextCheck(PThreadext pthreadext) /* can't check ID */ CHECKD_NOSIG(Ring, &pthreadext->threadRing); CHECKD_NOSIG(Ring, &pthreadext->idRing); - if (pthreadext->suspendedMFC == NULL) { + if (pthreadext->context == NULL) { /* not suspended */ CHECKL(RingIsSingle(&pthreadext->threadRing)); CHECKL(RingIsSingle(&pthreadext->idRing)); @@ -191,7 +181,7 @@ extern Bool PThreadextCheck(PThreadext pthreadext) RING_FOR(node, &pthreadext->idRing, next) { PThreadext pt = RING_ELT(PThreadext, idRing, node); CHECKL(pt->id == pthreadext->id); - CHECKL(pt->suspendedMFC == pthreadext->suspendedMFC); + CHECKL(pt->context == pthreadext->context); } } status = pthread_mutex_unlock(&pthreadextMut); @@ -203,7 +193,7 @@ extern Bool PThreadextCheck(PThreadext pthreadext) /* PThreadextInit -- Initialize a pthreadext */ -extern void PThreadextInit(PThreadext pthreadext, pthread_t id) +void PThreadextInit(PThreadext pthreadext, pthread_t id) { int status; @@ -212,7 +202,7 @@ extern void PThreadextInit(PThreadext pthreadext, pthread_t id) AVER(status == 0); pthreadext->id = id; - pthreadext->suspendedMFC = NULL; + pthreadext->context = NULL; RingInit(&pthreadext->threadRing); RingInit(&pthreadext->idRing); pthreadext->sig = PThreadextSig; @@ -225,7 +215,7 @@ extern void PThreadextInit(PThreadext pthreadext, pthread_t id) * See */ -extern void PThreadextFinish(PThreadext pthreadext) +void PThreadextFinish(PThreadext pthreadext) { int status; @@ -234,7 +224,7 @@ extern void PThreadextFinish(PThreadext pthreadext) status = pthread_mutex_lock(&pthreadextMut); AVER(status == 0); - if(pthreadext->suspendedMFC == NULL) { + if(pthreadext->context == NULL) { AVER(RingIsSingle(&pthreadext->threadRing)); AVER(RingIsSingle(&pthreadext->idRing)); } else { @@ -259,7 +249,7 @@ extern void PThreadextFinish(PThreadext pthreadext) * See */ -Res PThreadextSuspend(PThreadext target, MutatorFaultContext *contextReturn) +Res PThreadextSuspend(PThreadext target, MutatorContext *contextReturn) { Ring node, next; Res res; @@ -267,7 +257,7 @@ Res PThreadextSuspend(PThreadext target, MutatorFaultContext *contextReturn) AVERT(PThreadext, target); AVER(contextReturn != NULL); - AVER(target->suspendedMFC == NULL); /* multiple suspends illegal */ + AVER(target->context == NULL); /* multiple suspends illegal */ /* Serialize access to suspend, makes life easier */ status = pthread_mutex_lock(&pthreadextMut); @@ -281,7 +271,7 @@ Res PThreadextSuspend(PThreadext target, MutatorFaultContext *contextReturn) PThreadext alreadySusp = RING_ELT(PThreadext, threadRing, node); if (alreadySusp->id == target->id) { RingAppend(&alreadySusp->idRing, &target->idRing); - target->suspendedMFC = alreadySusp->suspendedMFC; + target->context = alreadySusp->context; goto noteSuspended; } } @@ -303,9 +293,9 @@ Res PThreadextSuspend(PThreadext target, MutatorFaultContext *contextReturn) } noteSuspended: - AVER(target->suspendedMFC != NULL); + AVER(target->context != NULL); RingAppend(&suspendedRing, &target->threadRing); - *contextReturn = target->suspendedMFC; + *contextReturn = target->context; res = ResOK; unlock: @@ -328,7 +318,7 @@ Res PThreadextResume(PThreadext target) AVERT(PThreadext, target); AVER(pthreadextModuleInitialized); /* must have been a prior suspend */ - AVER(target->suspendedMFC != NULL); + AVER(target->context != NULL); /* Serialize access to suspend, makes life easier. */ status = pthread_mutex_lock(&pthreadextMut); @@ -354,7 +344,7 @@ Res PThreadextResume(PThreadext target) noteResumed: /* Remove the thread from the suspended ring */ RingRemove(&target->threadRing); - target->suspendedMFC = NULL; + target->context = NULL; res = ResOK; unlock: @@ -366,7 +356,7 @@ Res PThreadextResume(PThreadext target) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/pthrdext.h b/mps/code/pthrdext.h index 3a28367126e..f2672340cc6 100644 --- a/mps/code/pthrdext.h +++ b/mps/code/pthrdext.h @@ -1,7 +1,7 @@ /* pthreadext.h: POSIX THREAD EXTENSIONS * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. * * .readership: MM developers. * @@ -32,7 +32,7 @@ typedef struct PThreadextStruct *PThreadext; typedef struct PThreadextStruct { Sig sig; /* */ pthread_t id; /* Thread ID */ - MutatorFaultContext suspendedMFC; /* context if suspended */ + MutatorContext context; /* context if suspended */ RingStruct threadRing; /* ring of suspended threads */ RingStruct idRing; /* duplicate suspensions for id */ } PThreadextStruct; @@ -57,7 +57,7 @@ extern void PThreadextFinish(PThreadext pthreadext); /* PThreadextSuspend -- Suspend a pthreadext and return its context. */ extern Res PThreadextSuspend(PThreadext pthreadext, - MutatorFaultContext *contextReturn); + MutatorContext *contextReturn); /* PThreadextResume -- Resume a suspended pthreadext */ @@ -70,7 +70,7 @@ extern Res PThreadextResume(PThreadext pthreadext); /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/qs.c b/mps/code/qs.c index 2a62a5ef71a..43b539df457 100644 --- a/mps/code/qs.c +++ b/mps/code/qs.c @@ -1,7 +1,7 @@ /* qs.c: QUICKSORT * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 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 @@ -27,7 +27,7 @@ #include "mps.h" #include "mpsavm.h" #include "mpscamc.h" -#include "mpscmv.h" +#include "mpscmvff.h" #include "mpstd.h" #include /* printf */ @@ -120,7 +120,6 @@ static void cons(mps_word_t tag0, mps_addr_t value0, QSCell tail) reg[0] = (mps_addr_t)new; regtag[0] = QSRef; - return; } @@ -165,7 +164,6 @@ static void append(void) /* null out reg[1] */ regtag[1] = QSRef; reg[1] = (mps_addr_t)0; - return; } @@ -337,10 +335,9 @@ static void *go(void *p, size_t s) testlib_unused(p); testlib_unused(s); - die(mps_pool_create(&mpool, arena, mps_class_mv(), - (size_t)65536, sizeof(QSCellStruct) * 1000, - (size_t)65536), - "MVCreate"); + die(mps_pool_create_k(&mpool, arena, mps_class_mvff(), mps_args_none), + "pool create"); + die(mps_fmt_create_A(&format, arena, &fmt_A_s), "FormatCreate"); die(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); die(mps_pool_create(&pool, arena, mps_class_amc(), format, chain), @@ -394,7 +391,6 @@ static void pad(mps_addr_t base, size_t size) cdie(size >= 2*sizeof(mps_word_t), "pad size 2"); object[0] = QSPadMany; object[1] = size; - return; } @@ -418,7 +414,7 @@ static mps_res_t scan1(mps_ss_t ss, mps_addr_t *objectIO) if(res != MPS_RES_OK) return res; cell->value = addr; - /* fall */ + /* fall through */ case QSInt: fixTail: @@ -457,6 +453,7 @@ static mps_res_t scan1(mps_ss_t ss, mps_addr_t *objectIO) static mps_res_t scan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) { + Insist(mps_arena_busy(arena)); while(base < limit) { mps_res_t res; @@ -540,7 +537,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2014 Ravenbrook Limited . + * Copyright (c) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/range.c b/mps/code/range.c index 8b8f1c8bf8a..f0ff476d061 100644 --- a/mps/code/range.c +++ b/mps/code/range.c @@ -1,7 +1,7 @@ /* range.c: ADDRESS RANGE IMPLEMENTATION * * $Id$ - * Copyright (c) 2013 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2013-2018 Ravenbrook Limited. See end of file for license. * * .design: */ @@ -39,6 +39,10 @@ void RangeInitSize(Range range, Addr base, Size size) void RangeFinish(Range range) { AVERT(Range, range); + /* Make range invalid and recognisably so, since Range doesn't have + a signature. */ + range->limit = (Addr)0; + range->base = (Addr)(Word)0xF191583D; /* FINISHED */ } Res RangeDescribe(Range range, mps_lib_FILE *stream, Count depth) @@ -104,6 +108,20 @@ Addr (RangeLimit)(Range range) return RangeLimit(range); } +void (RangeSetBase)(Range range, Addr addr) +{ + AVERT(Range, range); + AVER(addr >= RangeBase(range)); + RangeSetBase(range, addr); +} + +void (RangeSetLimit)(Range range, Addr addr) +{ + AVERT(Range, range); + AVER(addr <= RangeLimit(range)); + RangeSetLimit(range, addr); +} + Size (RangeSize)(Range range) { AVERT(Range, range); @@ -119,7 +137,7 @@ void RangeCopy(Range to, Range from) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2013 Ravenbrook Limited . + * Copyright (C) 2013-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/range.h b/mps/code/range.h index ac262c98c1a..a4a2db3d8a3 100644 --- a/mps/code/range.h +++ b/mps/code/range.h @@ -18,6 +18,8 @@ #define RangeBase(range) ((range)->base) #define RangeLimit(range) ((range)->limit) +#define RangeSetBase(range, addr) BEGIN ((range)->base = (addr)); END +#define RangeSetLimit(range, addr) BEGIN ((range)->limit = (addr)); END #define RangeSize(range) (AddrOffset(RangeBase(range), RangeLimit(range))) #define RangeContains(range, addr) ((range)->base <= (addr) && (addr) < (range)->limit) #define RangeIsEmpty(range) (RangeSize(range) == 0) @@ -33,11 +35,12 @@ extern Bool RangesNest(Range outer, Range inner); extern Bool RangesEqual(Range range1, Range range2); extern Addr (RangeBase)(Range range); extern Addr (RangeLimit)(Range range); +extern void (RangeSetBase)(Range range, Addr addr); +extern void (RangeSetLimit)(Range range, Addr addr); extern Size (RangeSize)(Range range); extern void RangeCopy(Range to, Range from); - -/* Types */ +/* RangeStruct -- address range */ typedef struct RangeStruct { Addr base; diff --git a/mps/code/prmci6fr.c b/mps/code/rangetree.c similarity index 56% rename from mps/code/prmci6fr.c rename to mps/code/rangetree.c index b1c1a67590f..4db8262fa15 100644 --- a/mps/code/prmci6fr.c +++ b/mps/code/rangetree.c @@ -1,60 +1,86 @@ -/* prmci6li.c: PROTECTION MUTATOR CONTEXT x64 (FREEBSD) +/* rangetree.c -- binary trees of address ranges * * $Id$ - * 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. - * - * - * ASSUMPTIONS - * - * .sp: The stack pointer in the context is RSP. - * - * .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. + * Copyright (C) 2016-2018 Ravenbrook Limited. See end of file for license. */ -#include "prmcix.h" -#include "prmci6.h" - -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 +#include "rangetree.h" +#include "tree.h" +#include "range.h" +#include "mpm.h" -Addr MutatorFaultContextSP(MutatorFaultContext mfc) +void RangeTreeInit(RangeTree rangeTree, Addr base, Addr limit) { - return (Addr)mfc->ucontext->uc_mcontext.mc_rsp; /* .sp */ + AVER(rangeTree != NULL); + TreeInit(RangeTreeTree(rangeTree)); + RangeInit(RangeTreeRange(rangeTree), base, limit); + AVERT(RangeTree, rangeTree); } -Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext mfc, - mps_area_scan_t scan_area, - void *closure) +void RangeTreeInitFromRange(RangeTree rangeTree, Range range) { - Res res; + AVER(rangeTree != NULL); + TreeInit(RangeTreeTree(rangeTree)); + RangeCopy(RangeTreeRange(rangeTree), range); + AVERT(RangeTree, rangeTree); +} - /* This scans the root registers (.context.regroots). It also unnecessarily - scans the rest of the context. The optimisation to scan only relevant - parts would be machine dependent. */ - res = TraceScanArea( - ss, - (Word *)mfc->ucontext, - (Word *)((char *)mfc->ucontext + sizeof(*(mfc->ucontext))), - scan_area, closure - ); - return res; +Bool RangeTreeCheck(RangeTree rangeTree) +{ + CHECKL(rangeTree != NULL); + CHECKD_NOSIG(Tree, RangeTreeTree(rangeTree)); + CHECKD_NOSIG(Range, RangeTreeRange(rangeTree)); + return TRUE; +} + + +void RangeTreeFinish(RangeTree rangeTree) +{ + AVERT(RangeTree, rangeTree); + TreeFinish(RangeTreeTree(rangeTree)); + RangeFinish(RangeTreeRange(rangeTree)); +} + + +/* RangeTreeCompare -- Compare key to [base,limit) + * + * See + */ + +Compare RangeTreeCompare(Tree tree, TreeKey key) +{ + Addr base1, base2, limit2; + RangeTree block; + + AVERT_CRITICAL(Tree, tree); + AVER_CRITICAL(tree != TreeEMPTY); + AVER_CRITICAL(key != NULL); + + base1 = RangeTreeBaseOfKey(key); + block = RangeTreeOfTree(tree); + base2 = RangeTreeBase(block); + limit2 = RangeTreeLimit(block); + + if (base1 < base2) + return CompareLESS; + else if (base1 >= limit2) + return CompareGREATER; + else + return CompareEQUAL; +} + +TreeKey RangeTreeKey(Tree tree) +{ + return RangeTreeKeyOfBaseVar(RangeTreeBase(RangeTreeOfTree(tree))); } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2016-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/rangetree.h b/mps/code/rangetree.h new file mode 100644 index 00000000000..4349c34117b --- /dev/null +++ b/mps/code/rangetree.h @@ -0,0 +1,94 @@ +/* rangetree.c -- binary trees of address ranges + * + * $Id$ + * Copyright (C) 2016-2018 Ravenbrook Limited. See end of file for license. + */ + +#ifndef rangetree_h +#define rangetree_h + +#include "mpmtypes.h" +#include "range.h" +#include "tree.h" + +#define RangeTreeTree(rangeTree) (&(rangeTree)->treeStruct) +#define RangeTreeRange(rangeTree) (&(rangeTree)->rangeStruct) +#define RangeTreeOfTree(tree) PARENT(RangeTreeStruct, treeStruct, tree) +#define RangeTreeOfRange(range) PARENT(RangeTreeStruct, rangeStruct, range) + +#define RangeTreeBase(block) RangeBase(RangeTreeRange(block)) +#define RangeTreeLimit(block) RangeLimit(RangeTreeRange(block)) +#define RangeTreeSetBase(block, addr) RangeSetBase(RangeTreeRange(block), addr) +#define RangeTreeSetLimit(block, addr) RangeSetLimit(RangeTreeRange(block), addr) +#define RangeTreeSize(block) RangeSize(RangeTreeRange(block)) + +extern void RangeTreeInit(RangeTree rangeTree, Addr base, Addr limit); +extern void RangeTreeInitFromRange(RangeTree rangeTree, Range range); +extern Bool RangeTreeCheck(RangeTree rangeTree); +extern void RangeTreeFinish(RangeTree rangeTree); + + +/* Compare and key functions for use with TreeFind, TreeInsert, etc. + * + * We pass the rangeTree base directly as a TreeKey (void *) assuming + * that Addr can be encoded, possibly breaking . + * On an exotic platform where this isn't true, pass the address of + * base: that is, add an &. + */ + +#define RangeTreeKeyOfBaseVar(baseVar) ((TreeKey)(baseVar)) +#define RangeTreeBaseOfKey(key) ((Addr)(key)) + +extern Compare RangeTreeCompare(Tree tree, TreeKey key); +extern TreeKey RangeTreeKey(Tree tree); + + +/* RangeTreeStruct -- address range in a tree */ + +typedef struct RangeTreeStruct { + TreeStruct treeStruct; + RangeStruct rangeStruct; +} RangeTreeStruct; + +#endif /* rangetree_h */ + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (C) 2016-2018 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/mps/code/ref.c b/mps/code/ref.c index 3f330315556..c9b2de07aa6 100644 --- a/mps/code/ref.c +++ b/mps/code/ref.c @@ -1,7 +1,7 @@ /* ref.c: REFERENCES * * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. * * .purpose: Implement operations on Ref, RefSet, ZoneSet, and Rank. * @@ -35,7 +35,7 @@ Bool RankSetCheck(RankSet rankSet) /* ZoneSetOfRange -- calculate the zone set of a range of addresses */ -RefSet ZoneSetOfRange(Arena arena, Addr base, Addr limit) +ZoneSet ZoneSetOfRange(Arena arena, Addr base, Addr limit) { Word zbase, zlimit; @@ -292,13 +292,9 @@ ZoneSet ZoneSetBlacklist(Arena arena) } - - - - /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2002 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/replay.c b/mps/code/replay.c deleted file mode 100644 index 957547565ff..00000000000 --- a/mps/code/replay.c +++ /dev/null @@ -1,228 +0,0 @@ -/* replay.c: Allocation replayer - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. - * - * $Id$ - */ - -#include "config.h" -/* override variety setting for EVENT */ -#define EVENT - -#include "eventcom.h" -#include "eventpro.h" -#include "eventrep.h" -#include "mpmtypes.h" - -#include /* for size_t */ -#include /* for printf */ -#include /* for va_list */ -#include /* for EXIT_FAILURE */ -#include /* for strcmp */ -#include "mpstd.h" - - -#if defined(MPS_OS_W3) && defined(MPS_ARCH_I6) -#define PRIuLONGEST "llu" -#define PRIXPTR "016llX" -typedef unsigned long long ulongest_t; -#else -#define PRIuLONGEST "lu" -#define PRIXPTR "08lX" -typedef unsigned long ulongest_t; -#endif - - -typedef unsigned long ulong; - - -/* command-line arguments */ - -static char *prog; /* program name */ - - -/* Globals */ - -static Word eventTime = 0; /* current event time */ - - -/* error -- error signalling */ - -ATTRIBUTE_FORMAT((printf, 1, 2)) -static void error(const char *format, ...) -{ - va_list args; - - fflush(stdout); /* sync */ - fprintf(stderr, "%s: @%"PRIuLONGEST" ", prog, (ulongest_t)eventTime); - va_start(args, format); - vfprintf(stderr, format, args); - fprintf(stderr, "\n"); - va_end(args); - exit(EXIT_FAILURE); -} - - -/* usage -- usage message */ - -static void usage(void) -{ - fprintf(stderr, - "Usage: %s [-f logfile] [-p] [-?]\n" - "See guide.mps.telemetry for instructions.\n", - prog); -} - - -/* usageError -- explain usage and error */ - -static void usageError(void) -{ - usage(); - error("Bad usage"); -} - - -/* parseArgs -- parse command line arguments, return log file name */ - -static char *parseArgs(int argc, char *argv[]) -{ - char *name = "mpsio.log"; - int i = 1; - - if (argc >= 1) - prog = argv[0]; - else - prog = "unknown"; - - while (i < argc) { /* consider argument i */ - if (argv[i][0] == '-') { /* it's an option argument */ - switch (argv[i][1]) { - case 'f': /* file name */ - ++ i; - if (i == argc) - usageError(); - else - name = argv[i]; - break; - case '?': case 'h': /* help */ - usage(); - break; - default: - usageError(); - } - } /* if option */ - ++ i; - } - return name; -} - - -/* readLog -- read and parse log */ - - -static void readLog(EventProc proc) -{ - while (TRUE) { - Event event; - Res res; - - res = EventRead(&event, proc); - if (res == ResFAIL) - break; /* eof */ - if (res != ResOK) - error("Truncated log"); - eventTime = event->any.clock; - EventRecord(proc, event, eventTime); - EventReplay(event, eventTime); - EventDestroy(proc, event); - } -} - - -/* logReader -- reader function for a file log */ - -static FILE *input; - -static Res logReader(void *file, void *p, size_t len) -{ - size_t n; - - n = fread(p, 1, len, (FILE *)file); - return (n < len) ? (feof((FILE *)file) ? ResFAIL : ResIO) : ResOK; -} - - -/* main */ - -int main(int argc, char *argv[]) -{ - char *filename; - EventProc proc; - Res res; - - filename = parseArgs(argc,argv); - - if (strcmp(filename, "-") == 0) - input = stdin; - else { - input = fopen(filename, "rb"); - if (input == NULL) - error("unable to open \"%s\"\n", filename); - } - - res = EventProcCreate(&proc, logReader, (void *)input); - if (res != ResOK) - error("Can't init EventProc module: error %d.", res); - - res = EventRepInit(); - if (res != ResOK) - error("Can't init EventRep module: error %d.", res); - - readLog(proc); - - EventRepFinish(); - EventProcDestroy(proc); - return EXIT_SUCCESS; -} - - -/* 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/mps/code/reserv.c b/mps/code/reserv.c deleted file mode 100644 index 91a2fd52559..00000000000 --- a/mps/code/reserv.c +++ /dev/null @@ -1,458 +0,0 @@ -/* reserv.c: ARENA RESERVOIR - * - * $Id$ - * 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 grain size. */ - -#include "mpm.h" - -SRCID(reserv, "$Id$"); - - -/* The reservoir pool is defined here. See */ - -#define PoolReservoir(pool) PARENT(ReservoirStruct, poolStruct, pool) - - -/* Management of tracts - * - * The reservoir maintains a linked list of tracts in arbitrary order. - * (see .improve.contiguous) - * - * Tracts are chained using the TractP field. */ - -#define resTractNext(tract) ((Tract)TractP((tract))) -#define resTractSetNext(tract, next) (TractSetP((tract), (void*)(next))) - - -#define reservoirArena(reservoir) (PoolArena(ReservoirPool(reservoir))) - - -/* ResPoolInit -- Reservoir pool init method */ - -static Res ResPoolInit(Pool pool, ArgList arg) -{ - AVER(pool != NULL); - - UNUSED(arg); - /* Caller will set sig and AVERT. */ - EVENT3(PoolInit, pool, PoolArena(pool), ClassOfPool(pool)); - return ResOK; -} - - -/* ResPoolFinish -- Reservoir pool finish method - * - * .reservoir.finish: This might be called from ArenaFinish, so the - * arena cannot be checked at this time. In order to avoid the check, - * insist that the reservoir is empty, by AVERing that the reserve list - * is NULL. */ - -static void ResPoolFinish(Pool pool) -{ - Reservoir reservoir; - - AVERT(Pool, pool); - reservoir = PoolReservoir(pool); - AVERT(Reservoir, reservoir); - AVER(reservoir->reserve == NULL); /* .reservoir.finish */ -} - - -/* ReservoirPoolClass -- Class definition */ - -DEFINE_POOL_CLASS(ReservoirPoolClass, this) -{ - INHERIT_CLASS(this, AbstractPoolClass); - this->name = "Reservoir"; - this->size = sizeof(ReservoirStruct); - this->offset = offsetof(ReservoirStruct, poolStruct); - this->init = ResPoolInit; - this->finish = ResPoolFinish; - AVERT(PoolClass, this); -} - - -/* ReservoirCheck -- Reservoir check method */ - -Bool ReservoirCheck(Reservoir reservoir) -{ - ReservoirPoolClass reservoircl = EnsureReservoirPoolClass(); - Arena arena; - Tract tract; - - CHECKS(Reservoir, reservoir); - 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) { - CHECKD_NOSIG(Tract, tract); - CHECKL(TractPool(tract) == ReservoirPool(reservoir)); - } - CHECKL(SizeIsArenaGrains(reservoir->reservoirLimit, arena)); - CHECKL(SizeIsArenaGrains(reservoir->reservoirSize, arena)); - CHECKL(reservoir->reservoirSize <= reservoir->reservoirLimit); - - return TRUE; -} - - -/* reservoirIsConsistent -- returns FALSE if the reservoir is corrupt */ - -ATTRIBUTE_UNUSED -static Bool reservoirIsConsistent(Reservoir reservoir) -{ - Size grainSize, size = 0; - Tract tract; - Pool pool; - Arena arena; - - arena = reservoirArena(reservoir); - pool = ReservoirPool(reservoir); - - /* Check that the size of the tracts matches reservoirSize */ - grainSize = ArenaGrainSize(arena); - tract = reservoir->reserve; - while (tract != NULL) { - AVERT(Tract, tract); - AVER(TractPool(tract) == pool); - tract = resTractNext(tract); - size += grainSize; - } - - if (size != reservoir->reservoirSize) - return FALSE; - - /* */ - return SizeIsAligned(reservoir->reservoirLimit, grainSize) - && SizeIsAligned(reservoir->reservoirSize, grainSize) - && (reservoir->reservoirLimit >= reservoir->reservoirSize); -} - - -/* ReservoirEnsureFull - * - * Ensures that the reservoir is the right size, by topping it up with - * fresh memory from the arena if possible. */ - -Res ReservoirEnsureFull(Reservoir reservoir) -{ - Size limit, size; - Pool pool; - Arena arena; - AVERT(Reservoir, reservoir); - arena = reservoirArena(reservoir); - - AVERT(Arena, arena); - size = ArenaGrainSize(arena); - limit = reservoir->reservoirLimit; - - /* optimize the common case of a full reservoir */ - if (reservoir->reservoirSize == limit) - return ResOK; - - pool = ReservoirPool(reservoir); - - /* really ought to try hard to allocate contiguous tracts */ - /* see .improve.contiguous */ - while (reservoir->reservoirSize < limit) { - Res res; - Addr base; - Tract tract; - res = ArenaAlloc(&base, LocusPrefDefault(), size, pool, FALSE); - if (res != ResOK) { - AVER(reservoirIsConsistent(reservoir)); - return res; - } - tract = TractOfBaseAddr(arena, base); - reservoir->reservoirSize += size; - resTractSetNext(tract, reservoir->reserve); - reservoir->reserve = tract; - } - AVER(reservoirIsConsistent(reservoir)); - return ResOK; -} - - -/* reservoirShrink -- Reduce the size of the reservoir */ - -static void reservoirShrink(Reservoir reservoir, Size want) -{ - Arena arena; - Pool pool; - Size size; - - pool = ReservoirPool(reservoir); - arena = reservoirArena(reservoir); - AVER(SizeIsArenaGrains(want, arena)); - AVER(reservoir->reservoirSize >= want); - - if (reservoir->reservoirSize == want) - return; - - /* Iterate over tracts, freeing them while reservoir is too big */ - size = ArenaGrainSize(arena); - while (reservoir->reservoirSize > want) { - Tract tract = reservoir->reserve; - AVER(tract != NULL); - reservoir->reserve = resTractNext(tract); - ArenaFree(TractBase(tract), size, pool); - reservoir->reservoirSize -= size; - } - AVER(reservoir->reservoirSize == want); - AVER(reservoirIsConsistent(reservoir)); -} - - -/* ReservoirWithdraw -- Attempt to supply memory from the reservoir */ - -Res ReservoirWithdraw(Addr *baseReturn, Tract *baseTractReturn, - Reservoir reservoir, Size size, Pool pool) -{ - Arena arena; - - AVER(baseReturn != NULL); - AVER(baseTractReturn != NULL); - AVERT(Reservoir, reservoir); - arena = reservoirArena(reservoir); - AVERT(Arena, 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 != ArenaGrainSize(arena)) - return ResMEMORY; - - if (size <= reservoir->reservoirSize) { - /* Return the first tract */ - Tract tract = reservoir->reserve; - Addr base; - AVER(tract != NULL); - base = TractBase(tract); - reservoir->reserve = resTractNext(tract); - reservoir->reservoirSize -= ArenaGrainSize(arena); - TractFinish(tract); - TractInit(tract, pool, base); - AVER(reservoirIsConsistent(reservoir)); - *baseReturn = base; - *baseTractReturn = tract; - return ResOK; - } - - AVER(reservoirIsConsistent(reservoir)); - return ResMEMORY; /* no suitable region in the reservoir */ -} - - -/* ReservoirDeposit -- Top up the reservoir */ - -Bool ReservoirDeposit(Reservoir reservoir, Addr *baseIO, Size *sizeIO) -{ - Pool respool; - Addr addr, limit; - Size reslimit; - Arena arena; - Tract tract; - Addr base; - Size size; - - AVERT(Reservoir, reservoir); - arena = reservoirArena(reservoir); - AVERT(Arena, arena); - 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) { - AVERT(Tract, tract); - if (reservoir->reservoirSize < reslimit) { - /* Reassign the tract to the reservoir pool */ - TractFinish(tract); - TractInit(tract, respool, addr); - reservoir->reservoirSize += ArenaGrainSize(arena); - resTractSetNext(tract, reservoir->reserve); - reservoir->reserve = tract; - } else { - *baseIO = addr; - *sizeIO = AddrOffset(base, limit); - AVER(reservoirIsConsistent(reservoir)); - return TRUE; - } - } - AVER(addr == limit); - AVER(reservoirIsConsistent(reservoir)); - return FALSE; -} - - -/* mutatorBufferCount -- returns the number of mutator buffers for the arena - * - * This should probably be in the pool module, but it's only used here. */ - -static Count mutatorBufferCount(Globals arena) -{ - Ring nodep, nextp; - Count count = 0; - - /* Iterate over all pools, and count the mutator buffers in each */ - RING_FOR(nodep, &arena->poolRing, nextp) { - Pool pool = RING_ELT(Pool, arenaRing, nodep); - Ring nodeb, nextb; - - AVERT(Pool, pool); - RING_FOR(nodeb, &pool->bufferRing, nextb) { - Buffer buff = RING_ELT(Buffer, poolRing, nodeb); - if (buff->isMutator) - count++; - } - } - return count; -} - - -/* ReservoirSetLimit -- Set the reservoir limit */ - -void ReservoirSetLimit(Reservoir reservoir, Size size) -{ - Size needed; - Arena arena; - AVERT(Reservoir, reservoir); - arena = reservoirArena(reservoir); - AVERT(Arena, arena); - - if (size > 0) { - Size wastage; - /* */ - wastage = ArenaGrainSize(arena) * mutatorBufferCount(ArenaGlobals(arena)); - /* */ - needed = SizeArenaGrains(size, arena) + wastage; - } else { - needed = 0; /* */ - } - - AVER(SizeIsArenaGrains(needed, arena)); - /* Emit event now, so subsequent change can be ascribed to it. */ - EVENT2(ReservoirLimitSet, arena, size); - - if (needed > reservoir->reservoirSize) { - /* Try to grow the reservoir */ - reservoir->reservoirLimit = needed; - (void)ReservoirEnsureFull(reservoir); - } else { - /* Shrink the reservoir */ - reservoirShrink(reservoir, needed); - reservoir->reservoirLimit = needed; - AVER(reservoirIsConsistent(reservoir)); - } -} - - -/* ReservoirLimit -- Return the reservoir limit */ - -Size ReservoirLimit(Reservoir reservoir) -{ - AVERT(Reservoir, reservoir); - AVER(reservoirIsConsistent(reservoir)); - return reservoir->reservoirLimit; -} - - -/* ReservoirAvailable -- Return the amount in the reservoir */ - -Size ReservoirAvailable(Reservoir reservoir) -{ - AVERT(Reservoir, reservoir); - (void)ReservoirEnsureFull(reservoir); - return reservoir->reservoirSize; -} - - -/* ReservoirInit -- Initialize a reservoir */ - -Res ReservoirInit(Reservoir reservoir, Arena arena) -{ - Res res; - - /* reservoir and arena are not initialized and can't be checked */ - reservoir->reservoirLimit = (Size)0; - reservoir->reservoirSize = (Size)0; - reservoir->reserve = NULL; - reservoir->sig = ReservoirSig; - /* initialize the reservoir pool, */ - res = PoolInit(ReservoirPool(reservoir), - arena, EnsureReservoirPoolClass(), argsNone); - if (res == ResOK) { - AVERT(Reservoir, reservoir); - } - return res; -} - - -/* ReservoirFinish -- Finish a reservoir */ - -void ReservoirFinish (Reservoir reservoir) -{ - PoolFinish(ReservoirPool(reservoir)); - reservoir->sig = SigInvalid; -} - - -/* 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/mps/code/ring.c b/mps/code/ring.c index 2f4be5066f2..9ceeecfa232 100644 --- a/mps/code/ring.c +++ b/mps/code/ring.c @@ -65,14 +65,14 @@ Bool RingIsSingle(Ring ring) * See */ -Size RingLength(Ring ring) +Count RingLength(Ring ring) { - Size size = 0; + Count length = 0; Ring node, next; AVERT(Ring, ring); RING_FOR(node, ring, next) - ++ size; - return size; + ++ length; + return length; } diff --git a/mps/code/ring.h b/mps/code/ring.h index 9cb915f4bf4..c7d154dbe5c 100644 --- a/mps/code/ring.h +++ b/mps/code/ring.h @@ -30,7 +30,7 @@ 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); +extern Count RingLength(Ring ring); /* .ring.init: See */ extern void (RingInit)(Ring ring); diff --git a/mps/code/root.c b/mps/code/root.c index 63445f0ef21..c592c8120ed 100644 --- a/mps/code/root.c +++ b/mps/code/root.c @@ -52,7 +52,7 @@ typedef struct RootStruct { Thread thread; /* passed to scan */ mps_area_scan_t scan_area;/* area scanner for stack and registers */ AreaScanUnion the; - Word *stackCold; /* cold end of stack */ + void *stackCold; /* cold end of stack */ } thread; struct { mps_fmt_scan_t scan; /* format-like scanner */ @@ -129,6 +129,14 @@ Bool RootCheck(Root root) scan. */ break; + case RootTHREAD: + CHECKD_NOSIG(Thread, root->the.thread.thread); /* */ + CHECKL(FUNCHECK(root->the.thread.scan_area)); + /* Can't check anything about closure as it could mean anything to + scan_area. */ + /* Can't check anything about stackCold. */ + break; + case RootTHREAD_TAGGED: CHECKD_NOSIG(Thread, root->the.thread.thread); /* */ CHECKL(FUNCHECK(root->the.thread.scan_area)); @@ -185,7 +193,7 @@ static Res rootCreate(Root *rootReturn, Arena arena, AVERT(RootVar, type); globals = ArenaGlobals(arena); - res = ControlAlloc(&p, arena, sizeof(RootStruct), FALSE); + res = ControlAlloc(&p, arena, sizeof(RootStruct)); if (res != ResOK) return res; root = (Root)p; /* Avoid pun */ diff --git a/mps/code/sac.c b/mps/code/sac.c index ab8bf867e3f..e7b13327ea1 100644 --- a/mps/code/sac.c +++ b/mps/code/sac.c @@ -149,8 +149,7 @@ Res SACCreate(SAC *sacReturn, Pool pool, Count classesCount, middleIndex = i + 1; /* there must exist another class at i+1 */ /* Allocate SAC */ - res = ControlAlloc(&p, PoolArena(pool), sacSize(middleIndex, classesCount), - FALSE); + res = ControlAlloc(&p, PoolArena(pool), sacSize(middleIndex, classesCount)); if(res != ResOK) goto failSACAlloc; sac = p; @@ -245,7 +244,7 @@ static void sacFind(Index *iReturn, Size *blockSizeReturn, /* SACFill -- alloc an object, and perhaps fill the cache */ -Res SACFill(Addr *p_o, SAC sac, Size size, Bool hasReservoirPermit) +Res SACFill(Addr *p_o, SAC sac, Size size) { Index i; Count blockCount, j; @@ -257,7 +256,6 @@ Res SACFill(Addr *p_o, SAC sac, Size size, Bool hasReservoirPermit) AVER(p_o != NULL); AVERT(SAC, sac); AVER(size != 0); - AVERT(Bool, hasReservoirPermit); esac = ExternalSACOfSAC(sac); sacFind(&i, &blockSize, sac, size); @@ -272,7 +270,7 @@ Res SACFill(Addr *p_o, SAC sac, Size size, Bool hasReservoirPermit) blockSize = SizeAlignUp(size, PoolAlignment(sac->pool)); for (j = 0, fl = esac->_freelists[i]._blocks; j <= blockCount; ++j) { - res = PoolAlloc(&p, sac->pool, blockSize, hasReservoirPermit); + res = PoolAlloc(&p, sac->pool, blockSize); if (res != ResOK) break; /* @@@@ ignoring shields for now */ diff --git a/mps/code/sac.h b/mps/code/sac.h index bd52a35daff..b60047e9ee3 100644 --- a/mps/code/sac.h +++ b/mps/code/sac.h @@ -44,7 +44,7 @@ typedef struct mps_sac_classes_s *SACClasses; extern Res SACCreate(SAC *sac_o, Pool pool, Count classesCount, SACClasses classes); extern void SACDestroy(SAC sac); -extern Res SACFill(Addr *p_o, SAC sac, Size size, Bool hasReservoirPermit); +extern Res SACFill(Addr *p_o, SAC sac, Size size); extern void SACEmpty(SAC sac, Addr p, Size size); extern void SACFlush(SAC sac); diff --git a/mps/code/sacss.c b/mps/code/sacss.c index d85b3ae5073..037d134fe55 100644 --- a/mps/code/sacss.c +++ b/mps/code/sacss.c @@ -1,11 +1,10 @@ /* sacss.c: SAC MANUAL ALLOC STRESS TEST * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 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" @@ -74,11 +73,12 @@ static mps_res_t stress(mps_arena_t arena, mps_align_t align, /* allocate a load of objects */ for (i = 0; i < testSetSIZE; ++i) { + mps_addr_t obj; ss[i] = (*size)(i); - - res = make((mps_addr_t *)&ps[i], sac, ss[i]); + res = make(&obj, sac, ss[i]); if (res != MPS_RES_OK) return res; + ps[i] = obj; if (ss[i] >= sizeof(ps[i])) *ps[i] = 1; /* Write something, so it gets swap. */ } @@ -113,17 +113,19 @@ static mps_res_t stress(mps_arena_t arena, mps_align_t align, } /* allocate some new objects */ for (i=testSetSIZE/2; i. + * Copyright (c) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/sc.h b/mps/code/sc.h deleted file mode 100644 index da4a9c8eac5..00000000000 --- a/mps/code/sc.h +++ /dev/null @@ -1,205 +0,0 @@ -/* sc.h: STACK CONTEXT - * - * $Id$ - * Copyright (c) 2012 Ravenbrook Limited. See end of file for license. - * - * Provides a context to hold the registers and stack pointer - * - * This file provide wrappers for using setjmp or some similar mechanism - * to save the current callee-saves on the stack. - * - * See http://info.ravenbrook.com/mail/2012/08/03/14-36-35/0/ and the rest of - * the thread for the origin of the idea. - * - * TODO: Make StackScan take a StackContext - */ - -#ifndef sc_h -#define sc_h - -#include "mpm.h" - - -/* StackContext -- holds the registers including a stack pointer - * - * This contains the callee-save registers and the stack pointer. - * - * This is used to save the registers after or on entry to the arena so that - * they can be scanned. - */ - -/* STACK_CONTEXT_SAVE - save the callee-saves and stack pointer - * - * This macro saves the callee-saves and stack pointer for the - * current function into the passed StackContext. The StackContext - * is no longer valid after the function returns. - * - * This needs to be a macro because the compiler may need to do - * setjmp magic. - */ - -/* StackContextStackHot - return the hot end of the stack from the stack context - * - * We assume the stack is full. In other words the stack top points at - * a word that contains a potential Ref. - */ - - -/* Mac OS X on 32-bit Intel built with Clang or GCC */ - -#if defined(MPS_PF_XCI3LL) || defined(MPS_PF_XCI3GC) - -#include - -typedef struct StackContextStruct { - jmp_buf jumpBuffer; -} StackContextStruct; - -/* See the implementation of _setjmp in - * */ - -#define JB_ESP 36 /* offset into the jmp_buf in bytes as defined in _setjmp.s */ - -#define STACK_CONTEXT_SAVE(sc) ((void)_setjmp((sc)->jumpBuffer)) - -#define StackContextSP(sc) ((Addr *)(sc)->jumpBuffer[JB_ESP/sizeof(int)]) - -/* On MacOS X the stackPointer can end up pointing above the StackContext - * which we assume to be stored on the stack because it is no longer - * needed once we have _longjmp()ed back. So take the minimum of the - * SP and the base of the StackContext structure. */ -#define StackContextStackHot(sc) \ - (StackContextSP(sc) < (Addr*)(sc) ? StackContextSP(sc) : (Addr*)(sc)) - - -/* Mac OS X on 64-bit Intel build with Clang or GCC */ - -#elif defined(MPS_PF_XCI6LL) || defined(MPS_PF_XCI6GC) - -#include - -/* We could use getcontext() from libunwind but that produces - * deprecation warnings. See - * - */ - -typedef struct StackContextStruct { - jmp_buf jumpBuffer; -} StackContextStruct; - -/* See the implementation of _setjmp in - * */ - -#define STACK_CONTEXT_SAVE(sc) ((void)_setjmp((sc)->jumpBuffer)) - -#define JB_RSP 16 /* offset into the jmp_buf in bytes as defined in _setjmp.s */ - -/* jmp_buf is an int[] but the stack pointer is 8 bytes so we need a cast */ -/* FIXME: possible aliasing problem */ -#define StackContextSP(sc) \ - (*(Addr **)((char *)(sc)->jumpBuffer+JB_RSP)) - -/* On MacOS X the stackPointer can end up pointing above the StackContext - * which we assume to be stored on the stack because it is no longer - * needed once we have _longjmp()ed back. So take the minimum of the - * SP and the base of the StackContext structure. */ -#define StackContextStackHot(sc) \ - (StackContextSP(sc) < (Addr*)(sc) ? StackContextSP(sc) : (Addr*)(sc)) - - -/* Windows on 32-bit Intel with Microsoft Visual Studio */ - -#elif defined(MPS_PF_W3I3MV) - -#include - -typedef struct StackContextStruct { - jmp_buf jumpBuffer; -} StackContextStruct; - -#define STACK_CONTEXT_SAVE(sc) ((void)setjmp((sc)->jumpBuffer)) - -#define StackContextStackHot(sc) \ - ((Addr *)((_JUMP_BUFFER *)(sc)->jumpBuffer)->Esp) - - -/* Windows on 64-bit Intel with Microsoft Visual Studio */ - -#elif defined(MPS_PF_W3I6MV) - -#include - -typedef struct StackContextStruct { - jmp_buf jumpBuffer; -} StackContextStruct; - -#define STACK_CONTEXT_SAVE(sc) ((void)setjmp((sc)->jumpBuffer)) - -#define StackContextStackHot(sc) \ - ((Addr *)((_JUMP_BUFFER *)(sc)->jumpBuffer)->Rsp) - - -#else - -/* TODO: implement this on other platforms in a safer way. - * Potentially the callee saves from the calling function could be spilled - * underneath the jmp_buf so returning the address of the jmp_buf for the - * stack top is not completely safe. - */ - -#include - -typedef struct StackContextStruct { - jmp_buf jumpBuffer; -} StackContextStruct; - -#define STACK_CONTEXT_SAVE(sc) ((void)setjmp((sc)->jumpBuffer)) - -#define StackContextStackHot(sc) ((Addr *)(sc)->jumpBuffer) - - -#endif /* platform defines */ - -#endif /* sc_h */ - - -/* C. COPYRIGHT AND LICENSE - * - * Copyright (C) 2001-2012 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/mps/code/seg.c b/mps/code/seg.c index 7007bca62b7..7fa0f6b0280 100644 --- a/mps/code/seg.c +++ b/mps/code/seg.c @@ -1,29 +1,14 @@ /* seg.c: SEGMENTS * * $Id$ - * Copyright (c) 2001-2015 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * * .design: The design for this module is . * * PURPOSE * - * .purpose: This is the implementation of the generic segment interface. - * It defines the interface functions and two useful segment classes: - * .purpose.class.seg: Class Seg is a class which is as simple - * as efficiency demands permit. (It includes fields for storing colour - * for efficiency). It may be subclassed by clients of the module. - * .purpose.class.seg-gc: Class GCSeg is a concrete class support all - * all current GC features, and providing full backwards compatibility - * with "old-style" segments. It may be subclassed by clients of the - * module. - * - * TRANSGRESSIONS - * - * .check.shield: The "pm", "sm", and "depth" fields are not checked by - * SegCheck, because I haven't spent time working out the invariants. - * We should certainly work them out, by studying , and - * assert things about shielding, protection, shield cache consistency, - * etc. richard 1997-04-03 + * .purpose: This is the implementation of the generic segment + * interface and the segment classes Seg, GCSeg and MutatorSeg. */ #include "tract.h" @@ -32,21 +17,12 @@ SRCID(seg, "$Id$"); -/* SegGCSeg -- convert generic Seg to GCSeg */ - -#define SegGCSeg(seg) ((GCSeg)(seg)) - -/* SegPoolRing -- Pool ring accessor */ - -#define SegPoolRing(seg) (&(seg)->poolRing) - - /* forward declarations */ static void SegFinish(Seg seg); -static Res SegInit(Seg seg, Pool pool, Addr base, Size size, - Bool withReservoirPermit, ArgList args); +static Res SegInit(Seg seg, SegClass klass, Pool pool, + Addr base, Size size, ArgList args); /* Generic interface support */ @@ -54,8 +30,8 @@ static Res SegInit(Seg seg, Pool pool, Addr base, Size size, /* SegAlloc -- allocate a segment from the arena */ -Res SegAlloc(Seg *segReturn, SegClass class, LocusPref pref, - Size size, Pool pool, Bool withReservoirPermit, ArgList args) +Res SegAlloc(Seg *segReturn, SegClass klass, LocusPref pref, + Size size, Pool pool, ArgList args) { Res res; Arena arena; @@ -64,29 +40,27 @@ Res SegAlloc(Seg *segReturn, SegClass class, LocusPref pref, void *p; AVER(segReturn != NULL); - AVERT(SegClass, class); + AVERT(SegClass, klass); AVERT(LocusPref, pref); AVER(size > (Size)0); AVERT(Pool, pool); - AVERT(Bool, withReservoirPermit); arena = PoolArena(pool); AVERT(Arena, arena); AVER(SizeIsArenaGrains(size, arena)); /* allocate the memory from the arena */ - res = ArenaAlloc(&base, pref, size, pool, withReservoirPermit); + res = ArenaAlloc(&base, pref, size, pool); if (res != ResOK) goto failArena; /* allocate the segment object from the control pool */ - res = ControlAlloc(&p, arena, class->size, withReservoirPermit); + res = ControlAlloc(&p, arena, klass->size); if (res != ResOK) goto failControl; seg = p; - seg->class = class; - res = SegInit(seg, pool, base, size, withReservoirPermit, args); + res = SegInit(seg, klass, pool, base, size, args); if (res != ResOK) goto failInit; @@ -95,7 +69,7 @@ Res SegAlloc(Seg *segReturn, SegClass class, LocusPref pref, return ResOK; failInit: - ControlFree(arena, seg, class->size); + ControlFree(arena, seg, klass->size); failControl: ArenaFree(base, size, pool); failArena: @@ -111,8 +85,7 @@ void SegFree(Seg seg) Arena arena; Pool pool; Addr base; - Size size; - SegClass class; + Size size, structSize; AVERT(Seg, seg); pool = SegPool(seg); @@ -121,36 +94,32 @@ void SegFree(Seg seg) AVERT(Arena, arena); base = SegBase(seg); size = SegSize(seg); - class = seg->class; + structSize = ClassOfPoly(Seg, seg)->size; SegFinish(seg); - ControlFree(arena, seg, class->size); + ControlFree(arena, seg, structSize); ArenaFree(base, size, pool); EVENT2(SegFree, arena, seg); - return; } /* SegInit -- initialize a segment */ -static Res SegInit(Seg seg, Pool pool, Addr base, Size size, - Bool withReservoirPermit, ArgList args) +static Res segAbsInit(Seg seg, Pool pool, Addr base, Size size, ArgList args) { - Tract tract; - Addr addr, limit; Arena arena; - SegClass class; - Res res; - + Addr addr, limit; + Tract tract; + AVER(seg != NULL); AVERT(Pool, pool); arena = PoolArena(pool); AVER(AddrIsArenaGrain(base, arena)); AVER(SizeIsArenaGrains(size, arena)); - class = seg->class; - AVERT(SegClass, class); - AVERT(Bool, withReservoirPermit); + AVERT(ArgList, args); + + NextMethod(Inst, Seg, init)(CouldBeA(Inst, seg)); limit = AddrAdd(base, size); seg->limit = limit; @@ -160,17 +129,16 @@ static Res SegInit(Seg seg, Pool pool, Addr base, Size size, seg->grey = TraceSetEMPTY; seg->pm = AccessSetEMPTY; seg->sm = AccessSetEMPTY; + seg->defer = WB_DEFER_INIT; seg->depth = 0; + seg->queued = FALSE; seg->firstTract = NULL; - - seg->sig = SegSig; /* set sig now so tract checks will see it */ - + RingInit(SegPoolRing(seg)); + TRACT_FOR(tract, addr, arena, base, limit) { AVERT(Tract, 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); @@ -180,68 +148,73 @@ static Res SegInit(Seg seg, Pool pool, Addr base, Size size, } AVER(addr == seg->limit); - RingInit(SegPoolRing(seg)); + SetClassOfPoly(seg, CLASS(Seg)); + seg->sig = SegSig; + AVERC(Seg, seg); - /* Class specific initialization comes last */ - res = class->init(seg, pool, base, size, withReservoirPermit, args); + RingAppend(&pool->segRing, SegPoolRing(seg)); + + return ResOK; +} + +static Res SegInit(Seg seg, SegClass klass, Pool pool, Addr base, Size size, ArgList args) +{ + Res res; + + AVERT(SegClass, klass); + + /* Klass specific initialization comes last */ + res = klass->init(seg, pool, base, size, args); if (res != ResOK) - goto failInit; + return res; AVERT(Seg, seg); - RingAppend(&pool->segRing, SegPoolRing(seg)); - return ResOK; -failInit: - RingFinish(SegPoolRing(seg)); - TRACT_FOR(tract, addr, arena, base, limit) { - AVERT(Tract, tract); - TRACT_UNSET_SEG(tract); - } - seg->sig = SigInvalid; - return res; + return ResOK; } /* SegFinish -- finish a segment */ -static void SegFinish(Seg seg) +static void segAbsFinish(Inst inst) { + Seg seg = MustBeA(Seg, inst); Arena arena; Addr addr, limit; Tract tract; - SegClass class; AVERT(Seg, seg); - class = seg->class; - AVERT(SegClass, class); + + RingRemove(SegPoolRing(seg)); arena = PoolArena(SegPool(seg)); + + /* TODO: It would be good to avoid deprotecting segments eagerly + when we free them, especially if they're going to be + unmapped. This would require tracking of protection independent + of the existence of a SegStruct. */ if (seg->sm != AccessSetEMPTY) { ShieldLower(arena, seg, seg->sm); } - /* Class specific finishing cames first */ - class->finish(seg); - seg->rankSet = RankSetEMPTY; /* See */ - ShieldFlush(PoolArena(SegPool(seg))); + AVER(seg->depth == 0); + if (seg->queued) + ShieldFlush(PoolArena(SegPool(seg))); + AVER(!seg->queued); limit = SegLimit(seg); TRACT_TRACT_FOR(tract, addr, arena, seg->firstTract, limit) { AVERT(Tract, tract); - TractSetWhite(tract, TraceSetEMPTY); TRACT_UNSET_SEG(tract); } AVER(addr == seg->limit); - RingRemove(SegPoolRing(seg)); RingFinish(SegPoolRing(seg)); - seg->sig = SigInvalid; - /* Check that the segment is not exposed, or in the shield */ /* cache (see ). */ AVER(seg->depth == 0); @@ -249,7 +222,15 @@ static void SegFinish(Seg seg) /* fund are not protected) */ AVER(seg->sm == AccessSetEMPTY); AVER(seg->pm == AccessSetEMPTY); - + + seg->sig = SigInvalid; + InstFinish(CouldBeA(Inst, seg)); +} + +static void SegFinish(Seg seg) +{ + AVERC(Seg, seg); + Method(Inst, seg, finish)(MustBeA(Inst, seg)); } @@ -267,7 +248,23 @@ void SegSetGrey(Seg seg, TraceSet grey) /* Don't dispatch to the class method if there's no actual change in greyness, or if the segment doesn't contain any references. */ if (grey != SegGrey(seg) && SegRankSet(seg) != RankSetEMPTY) - seg->class->setGrey(seg, grey); + Method(Seg, seg, setGrey)(seg, grey); + + EVENT3(SegSetGrey, PoolArena(SegPool(seg)), seg, grey); +} + + +/* SegFlip -- update barriers for trace that's about to flip */ + +void SegFlip(Seg seg, Trace trace) +{ + AVERT(Seg, seg); + AVERT(Trace, trace); + + /* Don't dispatch to the class method unless the segment is grey for + the trace that's about to flip, and contains references. */ + if (TraceSetIsMember(SegGrey(seg), trace) && SegRankSet(seg) != RankSetEMPTY) + Method(Seg, seg, flip)(seg, trace); } @@ -280,7 +277,7 @@ void SegSetWhite(Seg seg, TraceSet white) { AVERT(Seg, seg); AVERT(TraceSet, white); - seg->class->setWhite(seg, white); + Method(Seg, seg, setWhite)(seg, white); } @@ -296,7 +293,7 @@ void SegSetRankSet(Seg seg, RankSet rankSet) AVERT(Seg, seg); AVERT(RankSet, rankSet); AVER(rankSet != RankSetEMPTY || SegSummary(seg) == RefSetEMPTY); - seg->class->setRankSet(seg, rankSet); + Method(Seg, seg, setRankSet)(seg, rankSet); } @@ -314,7 +311,7 @@ void SegSetSummary(Seg seg, RefSet summary) #endif if (summary != SegSummary(seg)) - seg->class->setSummary(seg, summary); + Method(Seg, seg, setSummary)(seg, summary); } @@ -331,16 +328,25 @@ void SegSetRankAndSummary(Seg seg, RankSet rankSet, RefSet summary) } #endif - seg->class->setRankSummary(seg, rankSet, summary); + Method(Seg, seg, setRankSummary)(seg, rankSet, summary); } -/* SegBuffer -- return the buffer of a segment */ +/* SegHasBuffer -- segment has a buffer? */ -Buffer SegBuffer(Seg seg) +Bool SegHasBuffer(Seg seg) +{ + Buffer buffer; + return SegBuffer(&buffer, seg); +} + + +/* SegBuffer -- get the buffer of a segment */ + +Bool SegBuffer(Buffer *bufferReturn, Seg seg) { AVERT_CRITICAL(Seg, seg); /* .seg.critical */ - return seg->class->buffer(seg); + return Method(Seg, seg, buffer)(bufferReturn, seg); } @@ -349,67 +355,109 @@ Buffer SegBuffer(Seg seg) void SegSetBuffer(Seg seg, Buffer buffer) { AVERT(Seg, seg); - if (buffer != NULL) - AVERT(Buffer, buffer); - seg->class->setBuffer(seg, buffer); + AVERT(Buffer, buffer); + Method(Seg, seg, setBuffer)(seg, buffer); +} + + +/* SegUnsetBuffer -- remove the buffer from a segment */ + +void SegUnsetBuffer(Seg seg) +{ + AVERT(Seg, seg); + Method(Seg, seg, unsetBuffer)(seg); +} + + +/* SegBufferScanLimit -- limit of scannable objects in segment */ + +Addr SegBufferScanLimit(Seg seg) +{ + Addr limit; + Buffer buf; + + AVERT(Seg, seg); + + if (!SegBuffer(&buf, seg)) { + /* Segment is unbuffered: entire segment scannable */ + limit = SegLimit(seg); + } else { + /* Segment is buffered: scannable up to limit of initialized objects. */ + limit = BufferScanLimit(buf); + } + return limit; +} + + +/* SegBufferFill -- allocate to a buffer from a segment */ + +Bool SegBufferFill(Addr *baseReturn, Addr *limitReturn, Seg seg, Size size, + RankSet rankSet) +{ + AVER(baseReturn != NULL); + AVER(limitReturn != NULL); + AVERT(Seg, seg); + AVER(size > 0); + AVERT(RankSet, rankSet); + return Method(Seg, seg, bufferFill)(baseReturn, limitReturn, + seg, size, rankSet); } /* SegDescribe -- describe a segment */ -Res SegDescribe(Seg seg, mps_lib_FILE *stream, Count depth) +static Res segAbsDescribe(Inst inst, mps_lib_FILE *stream, Count depth) { + Seg seg = CouldBeA(Seg, inst); Res res; Pool pool; - if (!TESTT(Seg, seg)) - return ResFAIL; + if (!TESTC(Seg, seg)) + return ResPARAM; if (stream == NULL) - return ResFAIL; + return ResPARAM; + + res = NextMethod(Inst, Seg, describe)(inst, stream, depth); + if (res != ResOK) + return res; pool = SegPool(seg); - res = WriteF(stream, depth, - "Segment $P [$A,$A) {\n", (WriteFP)seg, - (WriteFA)SegBase(seg), (WriteFA)SegLimit(seg), - " class $P (\"$S\")\n", - (WriteFP)seg->class, (WriteFS)seg->class->name, - " pool $P ($U)\n", - (WriteFP)pool, (WriteFU)pool->serial, - " depth $U\n", seg->depth, - " pm", + res = WriteF(stream, depth + 2, + "base $A\n", (WriteFA)SegBase(seg), + "limit $A\n", (WriteFA)SegLimit(seg), + "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", + "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", + "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" : "", + "\n", NULL); if (res != ResOK) return res; - res = seg->class->describe(seg, stream, depth + 2); - if (res != ResOK) - return res; + return ResOK; +} - res = WriteF(stream, 0, "\n", NULL); - if (res != ResOK) - return res; - - res = WriteF(stream, depth, "} Segment $P\n", (WriteFP)seg, NULL); - return res; +Res SegDescribe(Seg seg, mps_lib_FILE *stream, Count depth) +{ + return Method(Inst, seg, describe)(MustBeA(Inst, seg), stream, depth); } @@ -556,10 +604,9 @@ Bool SegNext(Seg *segReturn, Arena arena, Seg seg) * See */ -Res SegMerge(Seg *mergedSegReturn, Seg segLo, Seg segHi, - Bool withReservoirPermit) +Res SegMerge(Seg *mergedSegReturn, Seg segLo, Seg segHi) { - SegClass class; + SegClass klass; Addr base, mid, limit; Arena arena; Res res; @@ -567,27 +614,26 @@ Res SegMerge(Seg *mergedSegReturn, Seg segLo, Seg segHi, AVER(NULL != mergedSegReturn); AVERT(Seg, segLo); AVERT(Seg, segHi); - class = segLo->class; - AVER(segHi->class == class); + klass = ClassOfPoly(Seg, segLo); + AVER(ClassOfPoly(Seg, segHi) == klass); AVER(SegPool(segLo) == SegPool(segHi)); base = SegBase(segLo); mid = SegLimit(segLo); limit = SegLimit(segHi); AVER(SegBase(segHi) == SegLimit(segLo)); - AVERT(Bool, withReservoirPermit); arena = PoolArena(SegPool(segLo)); - ShieldFlush(arena); /* see */ + if (segLo->queued || segHi->queued) + ShieldFlush(arena); /* see */ /* Invoke class-specific methods to do the merge */ - res = class->merge(segLo, segHi, base, mid, limit, - withReservoirPermit); + res = Method(Seg, segLo, merge)(segLo, segHi, base, mid, limit); if (ResOK != res) goto failMerge; - EVENT3(SegMerge, segLo, segHi, BOOLOF(withReservoirPermit)); + EVENT2(SegMerge, segLo, segHi); /* Deallocate segHi object */ - ControlFree(arena, segHi, class->size); + ControlFree(arena, segHi, klass->size); AVERT(Seg, segLo); *mergedSegReturn = segLo; return ResOK; @@ -605,20 +651,20 @@ Res SegMerge(Seg *mergedSegReturn, Seg segLo, Seg segHi, * See */ -Res SegSplit(Seg *segLoReturn, Seg *segHiReturn, Seg seg, Addr at, - Bool withReservoirPermit) +Res SegSplit(Seg *segLoReturn, Seg *segHiReturn, Seg seg, Addr at) { Addr base, limit; - SegClass class; + SegClass klass; Seg segNew; Arena arena; Res res; void *p; + Buffer buffer; AVER(NULL != segLoReturn); AVER(NULL != segHiReturn); AVERT(Seg, seg); - class = seg->class; + klass = ClassOfPoly(Seg, seg); arena = PoolArena(SegPool(seg)); base = SegBase(seg); limit = SegLimit(seg); @@ -626,23 +672,23 @@ Res SegSplit(Seg *segLoReturn, Seg *segHiReturn, Seg seg, Addr at, AVER(AddrIsArenaGrain(at, arena)); AVER(at > base); AVER(at < limit); - 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); + AVER(!SegBuffer(&buffer, seg) || BufferLimit(buffer) <= at); - ShieldFlush(arena); /* see */ + if (seg->queued) + ShieldFlush(arena); /* see */ + AVER(SegSM(seg) == SegPM(seg)); /* Allocate the new segment object from the control pool */ - res = ControlAlloc(&p, arena, class->size, withReservoirPermit); + res = ControlAlloc(&p, arena, klass->size); if (ResOK != res) goto failControl; segNew = p; /* Invoke class-specific methods to do the split */ - res = class->split(seg, segNew, base, at, limit, - withReservoirPermit); + res = Method(Seg, seg, split)(seg, segNew, base, at, limit); if (ResOK != res) goto failSplit; @@ -654,13 +700,149 @@ Res SegSplit(Seg *segLoReturn, Seg *segHiReturn, Seg seg, Addr at, return ResOK; failSplit: - ControlFree(arena, segNew, class->size); + ControlFree(arena, segNew, klass->size); failControl: AVERT(Seg, seg); /* check the original seg is still valid */ return res; } +/* SegAccess -- mutator read/write access to a segment */ + +Res SegAccess(Seg seg, Arena arena, Addr addr, + AccessSet mode, MutatorContext context) +{ + AVERT(Seg, seg); + AVERT(Arena, arena); + AVER(arena == PoolArena(SegPool(seg))); + AVER(SegBase(seg) <= addr); + AVER(addr < SegLimit(seg)); + AVERT(AccessSet, mode); + AVERT(MutatorContext, context); + + return Method(Seg, seg, access)(seg, arena, addr, mode, context); +} + + +/* SegWhiten -- whiten objects */ + +Res SegWhiten(Seg seg, Trace trace) +{ + AVERT(Seg, seg); + AVERT(Trace, trace); + AVER(PoolArena(SegPool(seg)) == trace->arena); + return Method(Seg, seg, whiten)(seg, trace); +} + + +/* SegGreyen -- greyen non-white objects */ + +void SegGreyen(Seg seg, Trace trace) +{ + AVERT(Seg, seg); + AVERT(Trace, trace); + AVER(PoolArena(SegPool(seg)) == trace->arena); + Method(Seg, seg, greyen)(seg, trace); +} + + +/* SegBlacken -- blacken grey objects without scanning */ + +void SegBlacken(Seg seg, TraceSet traceSet) +{ + AVERT(Seg, seg); + AVERT(TraceSet, traceSet); + Method(Seg, seg, blacken)(seg, traceSet); +} + + +/* SegScan -- scan a segment */ + +Res SegScan(Bool *totalReturn, Seg seg, ScanState ss) +{ + AVER(totalReturn != NULL); + AVERT(Seg, seg); + AVERT(ScanState, ss); + AVER(PoolArena(SegPool(seg)) == ss->arena); + + /* We check that either ss->rank is in the segment's + * ranks, or that ss->rank is exact. The check is more complicated if + * we actually have multiple ranks in a seg. + * See */ + AVER(ss->rank == RankEXACT || RankSetIsMember(SegRankSet(seg), ss->rank)); + + /* Should only scan segments which contain grey objects. */ + AVER(TraceSetInter(SegGrey(seg), ss->traces) != TraceSetEMPTY); + + return Method(Seg, seg, scan)(totalReturn, seg, ss); +} + + +/* SegFix* -- fix a reference to an object in this segment + * + * See . + */ + +Res SegFix(Seg seg, ScanState ss, Addr *refIO) +{ + AVERT_CRITICAL(Seg, seg); + AVERT_CRITICAL(ScanState, ss); + AVER_CRITICAL(refIO != NULL); + + /* Should only be fixing references to white segments. */ + AVER_CRITICAL(TraceSetInter(SegWhite(seg), ss->traces) != TraceSetEMPTY); + + return Method(Seg, seg, fix)(seg, ss, refIO); +} + +Res SegFixEmergency(Seg seg, ScanState ss, Addr *refIO) +{ + Res res; + + AVERT_CRITICAL(Seg, seg); + AVERT_CRITICAL(ScanState, ss); + AVER_CRITICAL(refIO != NULL); + + /* Should only be fixing references to white segments. */ + AVER_CRITICAL(TraceSetInter(SegWhite(seg), ss->traces) != TraceSetEMPTY); + + res = Method(Seg, seg, fixEmergency)(seg, ss, refIO); + AVER_CRITICAL(res == ResOK); + return res; +} + + +/* SegReclaim -- reclaim a segment */ + +void SegReclaim(Seg seg, Trace trace) +{ + AVERT_CRITICAL(Seg, seg); + AVERT_CRITICAL(Trace, trace); + AVER_CRITICAL(PoolArena(SegPool(seg)) == trace->arena); + + /* There shouldn't be any grey things left for this trace. */ + AVER_CRITICAL(!TraceSetIsMember(SegGrey(seg), trace)); + /* Should only be reclaiming segments which are still white. */ + AVER_CRITICAL(TraceSetIsMember(SegWhite(seg), trace)); + + Method(Seg, seg, reclaim)(seg, trace); +} + + +/* SegWalk -- walk objects in this segment */ + +void SegWalk(Seg seg, Format format, FormattedObjectsVisitor f, + void *p, size_t s) +{ + AVERT(Seg, seg); + AVERT(Format, format); + AVER(FUNCHECK(f)); + /* p and s are arbitrary values, hence can't be checked. */ + + Method(Seg, seg, walk)(seg, format, f, p, s); +} + + /* Class Seg -- The most basic segment class * * .seg.method.check: Many seg methods are lightweight and used @@ -678,6 +860,7 @@ Bool SegCheck(Seg seg) Pool pool; CHECKS(Seg, seg); + CHECKC(Seg, seg); CHECKL(TraceSetCheck(seg->white)); /* can't assume nailed is subset of white - mightn't be during whiten */ @@ -691,11 +874,12 @@ Bool SegCheck(Seg seg) CHECKL(AddrIsArenaGrain(TractBase(seg->firstTract), arena)); CHECKL(AddrIsArenaGrain(seg->limit, arena)); CHECKL(seg->limit > TractBase(seg->firstTract)); + /* CHECKL(BoolCheck(seq->queued)); */ - /* 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. */ + /* Each tract of the segment must agree about the segment and its + * pool. 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; @@ -706,7 +890,6 @@ Bool SegCheck(Seg seg) CHECKD_NOSIG(Tract, tract); CHECKL(TRACT_SEG(&trseg, tract)); CHECKL(trseg == seg); - CHECKL(TractWhite(tract) == seg->white); CHECKL(TractPool(tract) == pool); } CHECKL(addr == seg->limit); @@ -719,8 +902,17 @@ Bool SegCheck(Seg seg) /* CHECKL(RingNext(&seg->poolRing) != &seg->poolRing); */ CHECKD_NOSIG(Ring, &seg->poolRing); + + /* Shield invariants -- see design.mps.shield. */ - /* "pm", "sm", and "depth" not checked. See .check.shield. */ + /* The protection mode is never more than the shield mode + (design.mps.shield.inv.prot.shield). */ + CHECKL(BS_DIFF(seg->pm, seg->sm) == 0); + + /* All unsynced segments have positive depth or are in the queue + (design.mps.shield.inv.unsynced.depth). */ + CHECKL(seg->sm == seg->pm || seg->depth > 0 || seg->queued); + CHECKL(RankSetCheck(seg->rankSet)); if (seg->rankSet == RankSetEMPTY) { /* : If there are no refs */ @@ -739,43 +931,12 @@ Bool SegCheck(Seg seg) /* write shielded. */ /* CHECKL(seg->_summary == RefSetUNIV || (seg->_sm & AccessWRITE)); */ /* @@@@ What can be checked about the read barrier? */ + /* TODO: Need gcSegCheck? What does RankSet imply about being a gcSeg? */ } return TRUE; } -/* segTrivInit -- method to initialize the base fields of a segment */ - -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 */ - Arena arena; - - AVERT(Seg, seg); - AVERT(Pool, pool); - arena = PoolArena(pool); - AVER(AddrIsArenaGrain(base, arena)); - AVER(SizeIsArenaGrains(size, arena)); - AVER(SegBase(seg) == base); - AVER(SegSize(seg) == size); - AVER(SegPool(seg) == pool); - AVERT(Bool, reservoirPermit); - AVERT(ArgList, args); - UNUSED(args); - return ResOK; -} - - -/* segTrivFinish -- finish the base fields of a segment */ - -static void segTrivFinish(Seg seg) -{ - /* all the generic finishing happens in SegFinish */ - AVERT(Seg, seg); -} - - /* segNoSetGrey -- non-method to change the greyness of a segment */ static void segNoSetGrey(Seg seg, TraceSet grey) @@ -787,6 +948,16 @@ static void segNoSetGrey(Seg seg, TraceSet grey) } +/* segTrivFlip -- ignore trace that's about to flip */ + +static void segTrivFlip(Seg seg, Trace trace) +{ + AVERT(Seg, seg); + AVERT(Trace, trace); + AVER(seg->rankSet != RankSetEMPTY); +} + + /* segNoSetWhite -- non-method to change the whiteness of a segment */ static void segNoSetWhite(Seg seg, TraceSet white) @@ -830,11 +1001,12 @@ static void segNoSetRankSummary(Seg seg, RankSet rankSet, RefSet summary) /* segNoBuffer -- non-method to return the buffer of a segment */ -static Buffer segNoBuffer(Seg seg) +static Bool segNoBuffer(Buffer *bufferReturn, Seg seg) { AVERT(Seg, seg); + AVER(bufferReturn != NULL); NOTREACHED; - return NULL; + return FALSE; } @@ -843,18 +1015,49 @@ static Buffer segNoBuffer(Seg seg) static void segNoSetBuffer(Seg seg, Buffer buffer) { AVERT(Seg, seg); - if (buffer != NULL) - AVERT(Buffer, buffer); + AVERT(Buffer, buffer); NOTREACHED; } +/* segNoSetBuffer -- non-method to set the buffer of a segment */ + +static void segNoUnsetBuffer(Seg seg) +{ + AVERT(Seg, seg); + NOTREACHED; +} + + +/* segNoBufferFill -- non-method to fill buffer from segment */ + +static Bool segNoBufferFill(Addr *baseReturn, Addr *limitReturn, + Seg seg, Size size, RankSet rankSet) +{ + AVER(baseReturn != NULL); + AVER(limitReturn != NULL); + AVERT(Seg, seg); + AVER(size > 0); + AVERT(RankSet, rankSet); + NOTREACHED; + return FALSE; +} + + +/* segNoBufferEmpty -- non-method to empty buffer to segment */ + +static void segNoBufferEmpty(Seg seg, Buffer buffer) +{ + AVERT(Seg, seg); + AVERT(Buffer, buffer); + NOTREACHED; +} + /* segNoMerge -- merge method for segs which don't support merge */ static Res segNoMerge(Seg seg, Seg segHi, - Addr base, Addr mid, Addr limit, - Bool withReservoirPermit) + Addr base, Addr mid, Addr limit) { AVERT(Seg, seg); AVERT(Seg, segHi); @@ -862,7 +1065,6 @@ static Res segNoMerge(Seg seg, Seg segHi, AVER(SegLimit(seg) == mid); AVER(SegBase(segHi) == mid); AVER(SegLimit(segHi) == limit); - AVERT(Bool, withReservoirPermit); NOTREACHED; return ResFAIL; } @@ -875,8 +1077,7 @@ static Res segNoMerge(Seg seg, Seg segHi, */ static Res segTrivMerge(Seg seg, Seg segHi, - Addr base, Addr mid, Addr limit, - Bool withReservoirPermit) + Addr base, Addr mid, Addr limit) { Pool pool; Arena arena; @@ -896,7 +1097,6 @@ static Res segTrivMerge(Seg seg, Seg segHi, AVER(SegLimit(seg) == mid); AVER(SegBase(segHi) == mid); AVER(SegLimit(segHi) == limit); - AVERT(Bool, withReservoirPermit); /* .similar. */ AVER(seg->rankSet == segHi->rankSet); @@ -906,17 +1106,18 @@ static Res segTrivMerge(Seg seg, Seg segHi, AVER(seg->pm == segHi->pm); AVER(seg->sm == segHi->sm); AVER(seg->depth == segHi->depth); + AVER(seg->queued == segHi->queued); /* Neither segment may be exposed, or in the shield cache */ /* See & */ AVER(seg->depth == 0); + AVER(!seg->queued); /* no need to update fields which match. See .similar */ seg->limit = limit; TRACT_FOR(tract, addr, arena, mid, limit) { AVERT(Tract, tract); - AVER(TractHasSeg(tract)); - AVER(segHi == TractP(tract)); + AVER(segHi == TractSeg(tract)); AVER(TractPool(tract) == pool); TRACT_SET_SEG(tract, seg); } @@ -935,8 +1136,7 @@ static Res segTrivMerge(Seg seg, Seg segHi, /* segNoSplit -- split method for segs which don't support splitting */ static Res segNoSplit(Seg seg, Seg segHi, - Addr base, Addr mid, Addr limit, - Bool withReservoirPermit) + Addr base, Addr mid, Addr limit) { AVERT(Seg, seg); AVER(segHi != NULL); /* can't check fully, it's not initialized */ @@ -944,28 +1144,23 @@ static Res segNoSplit(Seg seg, Seg segHi, AVER(mid < limit); AVER(SegBase(seg) == base); AVER(SegLimit(seg) == limit); - AVERT(Bool, withReservoirPermit); NOTREACHED; return ResFAIL; - } /* segTrivSplit -- Basic Seg split method */ static Res segTrivSplit(Seg seg, Seg segHi, - Addr base, Addr mid, Addr limit, - Bool withReservoirPermit) + Addr base, Addr mid, Addr limit) { + Pool pool = SegPool(MustBeA(Seg, seg)); + Arena arena = PoolArena(pool); + SegClass klass; Tract tract; - Pool pool; Addr addr; - Arena arena; - AVERT(Seg, seg); AVER(segHi != NULL); /* can't check fully, it's not initialized */ - pool = SegPool(seg); - arena = PoolArena(pool); AVER(AddrIsArenaGrain(base, arena)); AVER(AddrIsArenaGrain(mid, arena)); AVER(AddrIsArenaGrain(limit, arena)); @@ -973,14 +1168,17 @@ static Res segTrivSplit(Seg seg, Seg segHi, AVER(mid < limit); AVER(SegBase(seg) == base); AVER(SegLimit(seg) == limit); - AVERT(Bool, withReservoirPermit); - /* Segment may not be exposed, or in the shield cache */ + /* Segment may not be exposed, or in the shield queue */ /* See & */ AVER(seg->depth == 0); + AVER(!seg->queued); /* Full initialization for segHi. Just modify seg. */ seg->limit = mid; + AVERT(Seg, seg); + + InstInit(CouldBeA(Inst, segHi)); segHi->limit = limit; segHi->rankSet = seg->rankSet; segHi->white = seg->white; @@ -989,15 +1187,13 @@ static Res segTrivSplit(Seg seg, Seg segHi, segHi->pm = seg->pm; segHi->sm = seg->sm; segHi->depth = seg->depth; + segHi->queued = seg->queued; segHi->firstTract = NULL; - segHi->class = seg->class; - segHi->sig = SegSig; RingInit(SegPoolRing(segHi)); TRACT_FOR(tract, addr, arena, mid, limit) { AVERT(Tract, tract); - AVER(TractHasSeg(tract)); - AVER(seg == TractP(tract)); + AVER(seg == TractSeg(tract)); AVER(TractPool(tract) == pool); TRACT_SET_SEG(tract, segHi); if (addr == mid) { @@ -1008,49 +1204,223 @@ static Res segTrivSplit(Seg seg, Seg segHi, } AVER(addr == segHi->limit); + klass = ClassOfPoly(Seg, seg); + SetClassOfPoly(segHi, klass); + segHi->sig = SegSig; + AVERC(Seg, segHi); + RingAppend(&pool->segRing, SegPoolRing(segHi)); - AVERT(Seg, seg); - AVERT(Seg, segHi); + return ResOK; } -/* segTrivDescribe -- Basic Seg description method */ - -static Res segTrivDescribe(Seg seg, mps_lib_FILE *stream, Count depth) +/* segNoAccess -- access method for non-GC segs + * + * Should be used (for the access method) by segment classes which do + * not expect to ever have pages which the mutator will fault on. That + * is, no protected pages, or only pages which are inaccessible by the + * mutator are protected. + */ +static Res segNoAccess(Seg seg, Arena arena, Addr addr, + AccessSet mode, MutatorContext context) { - Res res; + AVERT(Seg, seg); + AVERT(Arena, arena); + AVER(SegBase(seg) <= addr); + AVER(addr < SegLimit(seg)); + AVERT(AccessSet, mode); + AVERT(MutatorContext, context); + UNUSED(mode); + UNUSED(context); - 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; + NOTREACHED; + return ResUNIMPL; } -/* Class GCSeg -- Segment class with GC support +/* SegWholeAccess + * + * See also SegSingleAccess + * + * Should be used (for the access method) by segment classes which + * intend to handle page faults by scanning the entire segment and + * lowering the barrier. */ +Res SegWholeAccess(Seg seg, Arena arena, Addr addr, + AccessSet mode, MutatorContext context) +{ + AVERT(Seg, seg); + AVERT(Arena, arena); + AVER(arena == PoolArena(SegPool(seg))); + AVER(SegBase(seg) <= addr); + AVER(addr < SegLimit(seg)); + AVERT(AccessSet, mode); + AVERT(MutatorContext, context); + UNUSED(addr); + UNUSED(context); + TraceSegAccess(arena, seg, mode); + return ResOK; +} + + +/* SegSingleAccess + * + * See also ArenaRead, and SegWhileAccess. + * + * Handles page faults by attempting emulation. If the faulting + * instruction cannot be emulated then this function returns ResFAIL. + * + * Due to the assumptions made below, segment classes should only use + * this function if all words in an object are tagged or traceable. + * + * .single-access.assume.ref: It currently assumes that the address + * being faulted on contains a plain reference or a tagged + * non-reference. + * + * .single-access.improve.format: Later this will be abstracted + * through the client object format interface, so that no such + * assumption is necessary. + */ +Res SegSingleAccess(Seg seg, Arena arena, Addr addr, + AccessSet mode, MutatorContext context) +{ + AVERT(Seg, seg); + AVERT(Arena, arena); + AVER(arena == PoolArena(SegPool(seg))); + AVER(SegBase(seg) <= addr); + AVER(addr < SegLimit(seg)); + AVERT(AccessSet, mode); + AVERT(MutatorContext, context); + + if (MutatorContextCanStepInstruction(context)) { + Ref ref; + Res res; + + ShieldExpose(arena, seg); + + if(mode & SegSM(seg) & AccessREAD) { + /* Read access. */ + /* .single-access.assume.ref */ + /* .single-access.improve.format */ + ref = *(Ref *)addr; + /* .tagging: Check that the reference is aligned to a word boundary */ + /* (we assume it is not a reference otherwise). */ + if(WordIsAligned((Word)ref, sizeof(Word))) { + Rank rank; + /* See the note in TraceRankForAccess */ + /* (). */ + + rank = TraceRankForAccess(arena, seg); + TraceScanSingleRef(arena->flippedTraces, rank, arena, + seg, (Ref *)addr); + } + } + res = MutatorContextStepInstruction(context); + AVER(res == ResOK); + + /* Update SegSummary according to the possibly changed reference. */ + ref = *(Ref *)addr; + /* .tagging: ought to check the reference for a tag. But + * this is conservative. */ + SegSetSummary(seg, RefSetAdd(arena, SegSummary(seg), ref)); + + ShieldCover(arena, seg); + + return ResOK; + } else { + /* couldn't single-step instruction */ + return ResFAIL; + } +} + + +/* segNoWhiten -- whiten method for non-GC segs */ + +static Res segNoWhiten(Seg seg, Trace trace) +{ + AVERT(Seg, seg); + AVERT(Trace, trace); + AVER(PoolArena(SegPool(seg)) == trace->arena); + NOTREACHED; + return ResUNIMPL; +} + + +/* segNoGreyen -- greyen method for non-GC segs */ + +static void segNoGreyen(Seg seg, Trace trace) +{ + AVERT(Seg, seg); + AVERT(Trace, trace); + AVER(PoolArena(SegPool(seg)) == trace->arena); + NOTREACHED; +} + + +/* segNoBlacken -- blacken method for non-GC segs */ + +static void segNoBlacken(Seg seg, TraceSet traceSet) +{ + AVERT(Seg, seg); + AVERT(TraceSet, traceSet); + NOTREACHED; +} + + +/* segNoScan -- scan method for non-GC segs */ + +static Res segNoScan(Bool *totalReturn, Seg seg, ScanState ss) +{ + AVER(totalReturn != NULL); + AVERT(Seg, seg); + AVERT(ScanState, ss); + AVER(PoolArena(SegPool(seg)) == ss->arena); + NOTREACHED; + return ResUNIMPL; +} + + +/* segNoFix -- fix method for non-GC segs */ + +static Res segNoFix(Seg seg, ScanState ss, Ref *refIO) +{ + AVERT(Seg, seg); + AVERT(ScanState, ss); + AVER(refIO != NULL); + NOTREACHED; + return ResUNIMPL; +} + + +/* segNoReclaim -- reclaim method for non-GC segs */ + +static void segNoReclaim(Seg seg, Trace trace) +{ + AVERT(Seg, seg); + AVERT(Trace, trace); + AVER(PoolArena(SegPool(seg)) == trace->arena); + NOTREACHED; +} + + +/* segTrivWalk -- walk method for non-formatted segs */ + +static void segTrivWalk(Seg seg, Format format, FormattedObjectsVisitor f, + void *p, size_t s) +{ + AVERT(Seg, seg); + AVERT(Format, format); + AVER(FUNCHECK(f)); + /* p and s are arbitrary, hence can't be checked */ + UNUSED(p); + UNUSED(s); + NOOP; +} + + +/* Class GCSeg -- collectable segment class */ /* GCSegCheck -- check the integrity of a GCSeg */ @@ -1078,56 +1448,44 @@ Bool GCSegCheck(GCSeg gcseg) CHECKL(gcseg->summary == RefSetEMPTY); } + CHECKD_NOSIG(Ring, &gcseg->genRing); + return TRUE; } /* gcSegInit -- method to initialize a GC segment */ -static Res gcSegInit(Seg seg, Pool pool, Addr base, Size size, - Bool withReservoirPermit, ArgList args) +static Res gcSegInit(Seg seg, Pool pool, Addr base, Size size, ArgList args) { - SegClass super; GCSeg gcseg; - Arena arena; Res res; - AVERT(Seg, seg); - AVERT(Pool, pool); - arena = PoolArena(pool); - AVER(AddrIsArenaGrain(base, arena)); - AVER(SizeIsArenaGrains(size, arena)); - gcseg = SegGCSeg(seg); - AVER(&gcseg->segStruct == seg); - AVERT(Bool, withReservoirPermit); - /* Initialize the superclass fields first via next-method call */ - super = SEG_SUPERCLASS(GCSegClass); - res = super->init(seg, pool, base, size, withReservoirPermit, args); + res = NextMethod(Seg, GCSeg, init)(seg, pool, base, size, args); if (ResOK != res) return res; + gcseg = CouldBeA(GCSeg, seg); gcseg->summary = RefSetEMPTY; gcseg->buffer = NULL; RingInit(&gcseg->greyRing); - gcseg->sig = GCSegSig; + RingInit(&gcseg->genRing); + + SetClassOfPoly(seg, CLASS(GCSeg)); + gcseg->sig = GCSegSig; + AVERC(GCSeg, gcseg); - AVERT(GCSeg, gcseg); return ResOK; } /* gcSegFinish -- finish a GC segment */ -static void gcSegFinish(Seg seg) +static void gcSegFinish(Inst inst) { - SegClass super; - GCSeg gcseg; - - AVERT(Seg, seg); - gcseg = SegGCSeg(seg); - AVERT(GCSeg, gcseg); - AVER(&gcseg->segStruct == seg); + Seg seg = MustBeA(Seg, inst); + GCSeg gcseg = MustBeA(GCSeg, seg); if (SegGrey(seg) != TraceSetEMPTY) { RingRemove(&gcseg->greyRing); @@ -1138,13 +1496,13 @@ static void gcSegFinish(Seg seg) gcseg->sig = SigInvalid; /* Don't leave a dangling buffer allocating into hyperspace. */ - AVER(gcseg->buffer == NULL); + AVER(gcseg->buffer == NULL); /* */ RingFinish(&gcseg->greyRing); + RingFinish(&gcseg->genRing); /* finish the superclass fields last */ - super = SEG_SUPERCLASS(GCSegClass); - super->finish(seg); + NextMethod(Inst, GCSeg, finish)(inst); } @@ -1190,54 +1548,60 @@ static void gcSegSetGreyInternal(Seg seg, TraceSet oldGrey, TraceSet grey) RingRemove(&gcseg->greyRing); } - STATISTIC_STAT - ({ - TraceId ti; Trace trace; - TraceSet diff; + STATISTIC({ + TraceId ti; Trace trace; + TraceSet diff; - diff = TraceSetDiff(grey, oldGrey); - TRACE_SET_ITER(ti, trace, diff, arena) - ++trace->greySegCount; - if (trace->greySegCount > trace->greySegMax) - trace->greySegMax = trace->greySegCount; - TRACE_SET_ITER_END(ti, trace, diff, arena); - - diff = TraceSetDiff(oldGrey, grey); - TRACE_SET_ITER(ti, trace, diff, arena) - --trace->greySegCount; - TRACE_SET_ITER_END(ti, trace, diff, arena); - }); + diff = TraceSetDiff(grey, oldGrey); + TRACE_SET_ITER(ti, trace, diff, arena) + ++trace->greySegCount; + if (trace->greySegCount > trace->greySegMax) + trace->greySegMax = trace->greySegCount; + TRACE_SET_ITER_END(ti, trace, diff, arena); + diff = TraceSetDiff(oldGrey, grey); + TRACE_SET_ITER(ti, trace, diff, arena) + --trace->greySegCount; + TRACE_SET_ITER_END(ti, trace, diff, arena); + }); } /* gcSegSetGrey -- GCSeg method to change the greyness of a segment * - * Sets the segment greyness to the trace set grey and adjusts - * the shielding on the segment appropriately. + * Sets the segment greyness to the trace set grey. */ static void gcSegSetGrey(Seg seg, TraceSet grey) { - GCSeg gcseg; + AVERT_CRITICAL(Seg, seg); /* .seg.method.check */ + AVERT_CRITICAL(TraceSet, grey); /* .seg.method.check */ + AVER_CRITICAL(seg->rankSet != RankSetEMPTY); + + gcSegSetGreyInternal(seg, seg->grey, grey); /* do the work */ +} + + +/* mutatorSegSetGrey -- MutatorSeg method to change greyness of segment + * + * As gcSegSetGrey, but also raise or lower the read barrier. + */ + +static void mutatorSegSetGrey(Seg seg, TraceSet grey) +{ TraceSet oldGrey, flippedTraces; Arena arena; AVERT_CRITICAL(Seg, seg); /* .seg.method.check */ - AVERT_CRITICAL(TraceSet, grey); /* .seg.method.check */ - AVER(seg->rankSet != RankSetEMPTY); - gcseg = SegGCSeg(seg); - AVERT_CRITICAL(GCSeg, gcseg); - AVER_CRITICAL(&gcseg->segStruct == seg); - UNUSED(gcseg); - arena = PoolArena(SegPool(seg)); oldGrey = seg->grey; - gcSegSetGreyInternal(seg, oldGrey, grey); /* do the work */ + + NextMethod(Seg, MutatorSeg, setGrey)(seg, grey); /* The read barrier is raised when the segment is grey for */ /* some _flipped_ trace, i.e., is grey for a trace for which */ /* the mutator is black. */ + arena = PoolArena(SegPool(seg)); flippedTraces = arena->flippedTraces; if (TraceSetInter(oldGrey, flippedTraces) == TraceSetEMPTY) { if (TraceSetInter(grey, flippedTraces) != TraceSetEMPTY) @@ -1246,8 +1610,31 @@ static void gcSegSetGrey(Seg seg, TraceSet grey) if (TraceSetInter(grey, flippedTraces) == TraceSetEMPTY) ShieldLower(arena, seg, AccessREAD); } +} - EVENT3(SegSetGrey, arena, seg, grey); +/* mutatorSegFlip -- update barriers for a trace that's about to flip */ + +static void mutatorSegFlip(Seg seg, Trace trace) +{ + TraceSet flippedTraces; + Arena arena; + + NextMethod(Seg, MutatorSeg, flip)(seg, trace); + + arena = PoolArena(SegPool(seg)); + flippedTraces = arena->flippedTraces; + AVER(!TraceSetIsMember(flippedTraces, trace)); + + /* Raise the read barrier if the segment was not grey for any + currently flipped trace. */ + if (TraceSetInter(SegGrey(seg), flippedTraces) == TraceSetEMPTY) { + ShieldRaise(arena, seg, AccessREAD); + } else { + /* If the segment is grey for some currently flipped trace then + the read barrier must already have been raised, either in this + method or in mutatorSegSetGrey. */ + AVER(SegSM(seg) & AccessREAD); + } } @@ -1259,9 +1646,6 @@ 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 */ AVERT_CRITICAL(TraceSet, white); /* .seg.method.check */ @@ -1269,31 +1653,11 @@ static void gcSegSetWhite(Seg seg, TraceSet white) 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 */ - - AVERT_CRITICAL(Tract, tract); - AVER_CRITICAL(TRACT_SEG(&trseg, tract)); - AVER_CRITICAL(trseg == seg); - TractSetWhite(tract, BS_BITFIELD(Trace, white)); - } - AVER(addr == limit); - seg->white = BS_BITFIELD(Trace, white); } /* gcSegSetRankSet -- GCSeg method to set the rank set of a segment - * - * If the rank set is made non-empty then the segment's summary is - * now a subset of the mutator's (which is assumed to be RefSetUNIV) - * so the write barrier must be imposed on the segment. If the - * rank set is made empty then there are no longer any references - * on the segment so the barrier is removed. * * The caller must set the summary to empty before setting the rank * set to empty. The caller must set the rank set to non-empty before @@ -1302,80 +1666,105 @@ static void gcSegSetWhite(Seg seg, TraceSet white) static void gcSegSetRankSet(Seg seg, RankSet rankSet) { - GCSeg gcseg; - RankSet oldRankSet; - Arena arena; - AVERT_CRITICAL(Seg, seg); /* .seg.method.check */ AVERT_CRITICAL(RankSet, rankSet); /* .seg.method.check */ AVER_CRITICAL(rankSet == RankSetEMPTY || RankSetIsSingle(rankSet)); /* .seg.method.check */ - gcseg = SegGCSeg(seg); - AVERT_CRITICAL(GCSeg, gcseg); - AVER_CRITICAL(&gcseg->segStruct == seg); - arena = PoolArena(SegPool(seg)); - oldRankSet = seg->rankSet; seg->rankSet = BS_BITFIELD(Rank, rankSet); +} + + +/* mutatorSegSetRankSet -- MutatorSeg method to set rank set of segment + * + * As gcSegSetRankSet, but also sets or clears the write barrier on + * the segment. + * + * If the rank set is made non-empty then the segment's summary is now + * a subset of the mutator's (which is assumed to be RefSetUNIV) so + * the write barrier must be imposed on the segment. If the rank set + * is made empty then there are no longer any references on the + * segment so the barrier is removed. + */ + +static void mutatorSegSetRankSet(Seg seg, RankSet rankSet) +{ + RankSet oldRankSet; + + AVERT_CRITICAL(Seg, seg); /* .seg.method.check */ + oldRankSet = seg->rankSet; + + NextMethod(Seg, MutatorSeg, setRankSet)(seg, rankSet); if (oldRankSet == RankSetEMPTY) { if (rankSet != RankSetEMPTY) { - AVER(gcseg->summary == RefSetEMPTY); - ShieldRaise(arena, seg, AccessWRITE); + AVER_CRITICAL(SegGCSeg(seg)->summary == RefSetEMPTY); + ShieldRaise(PoolArena(SegPool(seg)), seg, AccessWRITE); } } else { if (rankSet == RankSetEMPTY) { - AVER(gcseg->summary == RefSetEMPTY); - ShieldLower(arena, seg, AccessWRITE); + AVER_CRITICAL(SegGCSeg(seg)->summary == RefSetEMPTY); + ShieldLower(PoolArena(SegPool(seg)), seg, AccessWRITE); } } } -/* gcSegSetSummary -- GCSeg method to change the summary on a segment +/* mutatorSegSyncWriteBarrier -- raise or lower write barrier on segment * - * In fact, we only need to raise the write barrier if the - * segment contains references, and its summary is strictly smaller - * than the summary of the unprotectable data (i.e. the mutator). - * We don't maintain such a summary, assuming that the mutator can - * access all references, so its summary is RefSetUNIV. + * We only need to raise the write barrier if the segment contains + * references, and its summary is strictly smaller than the summary of + * the unprotectable data (that is, the mutator). We don't maintain + * such a summary, assuming that the mutator can access all + * references, so its summary is RefSetUNIV. */ +static void mutatorSegSyncWriteBarrier(Seg seg) +{ + Arena arena = PoolArena(SegPool(seg)); + /* Can't check seg -- this function enforces invariants tested by SegCheck. */ + if (SegSummary(seg) == RefSetUNIV) + ShieldLower(arena, seg, AccessWRITE); + else + ShieldRaise(arena, seg, AccessWRITE); +} + + +/* gcSegSetSummary -- GCSeg method to change the summary on a segment */ + static void gcSegSetSummary(Seg seg, RefSet summary) { GCSeg gcseg; - RefSet oldSummary; - Arena arena; AVERT_CRITICAL(Seg, seg); /* .seg.method.check */ gcseg = SegGCSeg(seg); AVERT_CRITICAL(GCSeg, gcseg); AVER_CRITICAL(&gcseg->segStruct == seg); - arena = PoolArena(SegPool(seg)); - oldSummary = gcseg->summary; gcseg->summary = summary; - AVER(seg->rankSet != RankSetEMPTY); - - /* Note: !RefSetSuper is a test for a strict subset */ - if (!RefSetSuper(summary, RefSetUNIV)) { - if (RefSetSuper(oldSummary, RefSetUNIV)) - ShieldRaise(arena, seg, AccessWRITE); - } else { - if (!RefSetSuper(oldSummary, RefSetUNIV)) - ShieldLower(arena, seg, AccessWRITE); - } + AVER_CRITICAL(seg->rankSet != RankSetEMPTY); } +/* mutatorSegSetSummary -- MutatorSeg method to change summary on segment + * + * As gcSegSetSummary, but also raise or lower the write barrier. + */ + +static void mutatorSegSetSummary(Seg seg, RefSet summary) +{ + NextMethod(Seg, MutatorSeg, setSummary)(seg, summary); + mutatorSegSyncWriteBarrier(seg); +} + + + /* gcSegSetRankSummary -- GCSeg method to set both rank set and summary */ static void gcSegSetRankSummary(Seg seg, RankSet rankSet, RefSet summary) { GCSeg gcseg; - Bool wasShielded, willbeShielded; - Arena arena; AVERT_CRITICAL(Seg, seg); /* .seg.method.check */ AVERT_CRITICAL(RankSet, rankSet); /* .seg.method.check */ @@ -1386,27 +1775,23 @@ static void gcSegSetRankSummary(Seg seg, RankSet rankSet, RefSet summary) AVER_CRITICAL(&gcseg->segStruct == seg); /* rankSet == RankSetEMPTY implies summary == RefSetEMPTY */ - AVER(rankSet != RankSetEMPTY || summary == RefSetEMPTY); - - arena = PoolArena(SegPool(seg)); - - wasShielded = (seg->rankSet != RankSetEMPTY && gcseg->summary != RefSetUNIV); - willbeShielded = (rankSet != RankSetEMPTY && summary != RefSetUNIV); + AVER_CRITICAL(rankSet != RankSetEMPTY || summary == RefSetEMPTY); seg->rankSet = BS_BITFIELD(Rank, rankSet); gcseg->summary = summary; +} - if (willbeShielded && !wasShielded) { - ShieldRaise(arena, seg, AccessWRITE); - } else if (wasShielded && !willbeShielded) { - ShieldLower(arena, seg, AccessWRITE); - } +static void mutatorSegSetRankSummary(Seg seg, RankSet rankSet, RefSet summary) +{ + NextMethod(Seg, MutatorSeg, setRankSummary)(seg, rankSet, summary); + if (rankSet != RankSetEMPTY) + mutatorSegSyncWriteBarrier(seg); } /* gcSegBuffer -- GCSeg method to return the buffer of a segment */ -static Buffer gcSegBuffer(Seg seg) +static Bool gcSegBuffer(Buffer *bufferReturn, Seg seg) { GCSeg gcseg; @@ -1415,7 +1800,12 @@ static Buffer gcSegBuffer(Seg seg) AVERT_CRITICAL(GCSeg, gcseg); /* .seg.method.check */ AVER_CRITICAL(&gcseg->segStruct == seg); - return gcseg->buffer; + if (gcseg->buffer != NULL) { + *bufferReturn = gcseg->buffer; + return TRUE; + } + + return FALSE; } @@ -1436,6 +1826,15 @@ static void gcSegSetBuffer(Seg seg, Buffer buffer) } +/* gcSegUnsetBuffer -- GCSeg method to remove the buffer from a segment */ + +static void gcSegUnsetBuffer(Seg seg) +{ + GCSeg gcseg = MustBeA_CRITICAL(GCSeg, seg); /* .seg.method.check */ + gcseg->buffer = NULL; +} + + /* gcSegMerge -- GCSeg merge method * * .buffer: Can't merge two segments both with buffers. @@ -1443,10 +1842,8 @@ static void gcSegSetBuffer(Seg seg, Buffer buffer) */ static Res gcSegMerge(Seg seg, Seg segHi, - Addr base, Addr mid, Addr limit, - Bool withReservoirPermit) + Addr base, Addr mid, Addr limit) { - SegClass super; GCSeg gcseg, gcsegHi; TraceSet grey; RefSet summary; @@ -1465,32 +1862,39 @@ static Res gcSegMerge(Seg seg, Seg segHi, AVER(SegLimit(seg) == mid); AVER(SegBase(segHi) == mid); AVER(SegLimit(segHi) == limit); - AVERT(Bool, withReservoirPermit); buf = gcsegHi->buffer; /* any buffer on segHi must be reassigned */ AVER(buf == NULL || gcseg->buffer == NULL); /* See .buffer */ grey = SegGrey(segHi); /* check greyness */ AVER(SegGrey(seg) == grey); + /* Assume that the write barrier shield is being used to implement + the remembered set only, and so we can merge the shield and + protection modes by unioning the segment summaries. See also + design.mps.seg.merge.inv.similar. */ + summary = RefSetUnion(gcseg->summary, gcsegHi->summary); + SegSetSummary(seg, summary); + SegSetSummary(segHi, summary); + AVER(SegSM(seg) == SegSM(segHi)); + if (SegPM(seg) != SegPM(segHi)) { + /* This shield won't cope with a partially-protected segment, so + flush the shield queue to bring both halves in sync. See also + design.mps.seg.split-merge.shield.re-flush. */ + ShieldFlush(PoolArena(SegPool(seg))); + } + /* Merge the superclass fields via next-method call */ - super = SEG_SUPERCLASS(GCSegClass); - res = super->merge(seg, segHi, base, mid, limit, - withReservoirPermit); + res = NextMethod(Seg, GCSeg, merge)(seg, segHi, base, mid, limit); if (res != ResOK) goto failSuper; /* Update fields of gcseg. Finish gcsegHi. */ - summary = RefSetUnion(gcseg->summary, gcsegHi->summary); - if (summary != gcseg->summary) { - gcSegSetSummary(seg, summary); - /* */ - ShieldFlush(PoolArena(SegPool(seg))); - } - gcSegSetGreyInternal(segHi, grey, TraceSetEMPTY); gcsegHi->summary = RefSetEMPTY; gcsegHi->sig = SigInvalid; RingFinish(&gcsegHi->greyRing); + RingRemove(&gcsegHi->genRing); + RingFinish(&gcsegHi->genRing); /* Reassign any buffer that was connected to segHi */ if (NULL != buf) { @@ -1513,10 +1917,8 @@ static Res gcSegMerge(Seg seg, Seg segHi, /* gcSegSplit -- GCSeg split method */ static Res gcSegSplit(Seg seg, Seg segHi, - Addr base, Addr mid, Addr limit, - Bool withReservoirPermit) + Addr base, Addr mid, Addr limit) { - SegClass super; GCSeg gcseg, gcsegHi; Buffer buf; TraceSet grey; @@ -1525,13 +1927,11 @@ static Res gcSegSplit(Seg seg, Seg segHi, AVERT(Seg, seg); AVER(segHi != NULL); /* can't check fully, it's not initialized */ gcseg = SegGCSeg(seg); - gcsegHi = SegGCSeg(segHi); AVERT(GCSeg, gcseg); AVER(base < mid); AVER(mid < limit); AVER(SegBase(seg) == base); AVER(SegLimit(seg) == limit); - AVERT(Bool, withReservoirPermit); grey = SegGrey(seg); buf = gcseg->buffer; /* Look for buffer to reassign to segHi */ @@ -1545,16 +1945,17 @@ static Res gcSegSplit(Seg seg, Seg segHi, } /* Split the superclass fields via next-method call */ - super = SEG_SUPERCLASS(GCSegClass); - res = super->split(seg, segHi, base, mid, limit, - withReservoirPermit); + res = NextMethod(Seg, GCSeg, split)(seg, segHi, base, mid, limit); if (res != ResOK) goto failSuper; /* Full initialization for segHi. */ + gcsegHi = SegGCSeg(segHi); gcsegHi->summary = gcseg->summary; gcsegHi->buffer = NULL; RingInit(&gcsegHi->greyRing); + RingInit(&gcsegHi->genRing); + RingInsert(&gcseg->genRing, &gcsegHi->genRing); gcsegHi->sig = GCSegSig; gcSegSetGreyInternal(segHi, TraceSetEMPTY, grey); @@ -1575,38 +1976,79 @@ static Res gcSegSplit(Seg seg, Seg segHi, } +/* gcSegWhiten -- GCSeg white method */ + +static Res gcSegWhiten(Seg seg, Trace trace) +{ + AVERT(Seg, seg); + AVERT(Trace, trace); + AVER(PoolArena(SegPool(seg)) == trace->arena); + + SegSetWhite(seg, TraceSetAdd(SegWhite(seg), trace)); + + return ResOK; +} + + +/* gcSegGreyen -- GCSeg greyen method + * + * If we had a (partially) white segment, then other parts of the same + * segment might need to get greyed. In fact, all current pools only + * ever whiten a whole segment, so we never need to greyen any part of + * an already whitened segment. So we exclude white segments. + */ + +static void gcSegGreyen(Seg seg, Trace trace) +{ + AVERT(Seg, seg); + AVERT(Trace, trace); + AVER(PoolArena(SegPool(seg)) == trace->arena); + + if (!TraceSetIsMember(SegWhite(seg), trace)) + SegSetGrey(seg, TraceSetSingle(trace)); +} + + +/* gcSegTrivBlacken -- GCSeg trivial blacken method + * + * For segments which do not keep additional colour information. + */ + +static void gcSegTrivBlacken(Seg seg, TraceSet traceSet) +{ + AVERT(Seg, seg); + AVERT(TraceSet, traceSet); + NOOP; +} + + /* gcSegDescribe -- GCSeg description method */ -static Res gcSegDescribe(Seg seg, mps_lib_FILE *stream, Count depth) +static Res gcSegDescribe(Inst inst, mps_lib_FILE *stream, Count depth) { + GCSeg gcseg = CouldBeA(GCSeg, inst); Res res; - SegClass super; - GCSeg gcseg; - if (!TESTT(Seg, seg)) - return ResFAIL; + if (!TESTC(GCSeg, gcseg)) + return ResPARAM; if (stream == NULL) - return ResFAIL; - gcseg = SegGCSeg(seg); - if (!TESTT(GCSeg, gcseg)) - return ResFAIL; + return ResPARAM; /* Describe the superclass fields first via next-method call */ - super = SEG_SUPERCLASS(GCSegClass); - res = super->describe(seg, stream, depth); + res = NextMethod(Inst, GCSeg, describe)(inst, stream, depth); if (res != ResOK) return res; - res = WriteF(stream, depth, + res = WriteF(stream, depth + 2, "summary $W\n", (WriteFW)gcseg->summary, NULL); if (res != ResOK) return res; if (gcseg->buffer == NULL) { - res = WriteF(stream, depth, "buffer: NULL\n", NULL); + res = WriteF(stream, depth + 2, "buffer: NULL\n", NULL); } else { - res = BufferDescribe(gcseg->buffer, stream, depth); + res = BufferDescribe(gcseg->buffer, stream, depth + 2); } if (res != ResOK) return res; @@ -1617,46 +2059,86 @@ static Res gcSegDescribe(Seg seg, mps_lib_FILE *stream, Count depth) /* SegClassCheck -- check a segment class */ -Bool SegClassCheck(SegClass class) +Bool SegClassCheck(SegClass klass) { - CHECKD(ProtocolClass, &class->protocol); - CHECKL(class->name != NULL); /* Should be <= 6 char C identifier */ - CHECKL(class->size >= sizeof(SegStruct)); - CHECKL(FUNCHECK(class->init)); - CHECKL(FUNCHECK(class->finish)); - CHECKL(FUNCHECK(class->setGrey)); - CHECKL(FUNCHECK(class->setWhite)); - CHECKL(FUNCHECK(class->setRankSet)); - CHECKL(FUNCHECK(class->setRankSummary)); - CHECKL(FUNCHECK(class->merge)); - CHECKL(FUNCHECK(class->split)); - CHECKL(FUNCHECK(class->describe)); - CHECKS(SegClass, class); + CHECKD(InstClass, &klass->instClassStruct); + CHECKL(klass->size >= sizeof(SegStruct)); + CHECKL(FUNCHECK(klass->init)); + CHECKL(FUNCHECK(klass->setSummary)); + CHECKL(FUNCHECK(klass->buffer)); + CHECKL(FUNCHECK(klass->setBuffer)); + CHECKL(FUNCHECK(klass->unsetBuffer)); + CHECKL(FUNCHECK(klass->bufferFill)); + CHECKL(FUNCHECK(klass->bufferEmpty)); + CHECKL(FUNCHECK(klass->setGrey)); + CHECKL(FUNCHECK(klass->setWhite)); + CHECKL(FUNCHECK(klass->setRankSet)); + CHECKL(FUNCHECK(klass->setRankSummary)); + CHECKL(FUNCHECK(klass->merge)); + CHECKL(FUNCHECK(klass->split)); + CHECKL(FUNCHECK(klass->access)); + CHECKL(FUNCHECK(klass->whiten)); + CHECKL(FUNCHECK(klass->greyen)); + CHECKL(FUNCHECK(klass->blacken)); + CHECKL(FUNCHECK(klass->scan)); + CHECKL(FUNCHECK(klass->fix)); + CHECKL(FUNCHECK(klass->fixEmergency)); + CHECKL(FUNCHECK(klass->reclaim)); + CHECKL(FUNCHECK(klass->walk)); + + /* Check that segment classes override sets of related methods. */ + CHECKL((klass->init == segAbsInit) + == (klass->instClassStruct.finish == segAbsFinish)); + CHECKL((klass->init == gcSegInit) + == (klass->instClassStruct.finish == gcSegFinish)); + CHECKL((klass->merge == segTrivMerge) == (klass->split == segTrivSplit)); + CHECKL((klass->fix == segNoFix) == (klass->fixEmergency == segNoFix)); + CHECKL((klass->fix == segNoFix) == (klass->reclaim == segNoReclaim)); + + CHECKS(SegClass, klass); return TRUE; } /* SegClass -- the vanilla segment class definition */ -DEFINE_CLASS(SegClass, class) +DEFINE_CLASS(Inst, SegClass, klass) { - INHERIT_CLASS(&class->protocol, ProtocolClass); - class->name = "SEG"; - class->size = sizeof(SegStruct); - class->init = segTrivInit; - class->finish = segTrivFinish; - class->setSummary = segNoSetSummary; - class->buffer = segNoBuffer; - class->setBuffer = segNoSetBuffer; - class->setGrey = segNoSetGrey; - class->setWhite = segNoSetWhite; - class->setRankSet = segNoSetRankSet; - class->setRankSummary = segNoSetRankSummary; - class->merge = segTrivMerge; - class->split = segTrivSplit; - class->describe = segTrivDescribe; - class->sig = SegClassSig; - AVERT(SegClass, class); + INHERIT_CLASS(klass, SegClass, InstClass); + AVERT(InstClass, klass); +} + +DEFINE_CLASS(Seg, Seg, klass) +{ + INHERIT_CLASS(&klass->instClassStruct, Seg, Inst); + klass->instClassStruct.describe = segAbsDescribe; + klass->instClassStruct.finish = segAbsFinish; + klass->size = sizeof(SegStruct); + klass->init = segAbsInit; + klass->setSummary = segNoSetSummary; + klass->buffer = segNoBuffer; + klass->setBuffer = segNoSetBuffer; + klass->unsetBuffer = segNoUnsetBuffer; + klass->bufferFill = segNoBufferFill; + klass->bufferEmpty = segNoBufferEmpty; + klass->setGrey = segNoSetGrey; + klass->flip = segTrivFlip; + klass->setWhite = segNoSetWhite; + klass->setRankSet = segNoSetRankSet; + klass->setRankSummary = segNoSetRankSummary; + klass->merge = segTrivMerge; + klass->split = segTrivSplit; + klass->access = segNoAccess; + klass->whiten = segNoWhiten; + klass->greyen = segNoGreyen; + klass->blacken = segNoBlacken; + klass->scan = segNoScan; + klass->fix = segNoFix; + klass->fixEmergency = segNoFix; + klass->reclaim = segNoReclaim; + klass->walk = segTrivWalk; + klass->sig = SegClassSig; + AVERT(SegClass, klass); } @@ -1664,24 +2146,49 @@ DEFINE_CLASS(SegClass, class) typedef SegClassStruct GCSegClassStruct; -DEFINE_CLASS(GCSegClass, class) +DEFINE_CLASS(Seg, GCSeg, klass) { - INHERIT_CLASS(class, SegClass); - class->name = "GCSEG"; - class->size = sizeof(GCSegStruct); - class->init = gcSegInit; - class->finish = gcSegFinish; - class->setSummary = gcSegSetSummary; - class->buffer = gcSegBuffer; - class->setBuffer = gcSegSetBuffer; - class->setGrey = gcSegSetGrey; - class->setWhite = gcSegSetWhite; - class->setRankSet = gcSegSetRankSet; - class->setRankSummary = gcSegSetRankSummary; - class->merge = gcSegMerge; - class->split = gcSegSplit; - class->describe = gcSegDescribe; - AVERT(SegClass, class); + INHERIT_CLASS(klass, GCSeg, Seg); + klass->instClassStruct.describe = gcSegDescribe; + klass->instClassStruct.finish = gcSegFinish; + klass->size = sizeof(GCSegStruct); + klass->init = gcSegInit; + klass->setSummary = gcSegSetSummary; + klass->buffer = gcSegBuffer; + klass->setBuffer = gcSegSetBuffer; + klass->unsetBuffer = gcSegUnsetBuffer; + klass->setGrey = gcSegSetGrey; + klass->setWhite = gcSegSetWhite; + klass->setRankSet = gcSegSetRankSet; + klass->setRankSummary = gcSegSetRankSummary; + klass->merge = gcSegMerge; + klass->split = gcSegSplit; + klass->access = SegWholeAccess; + klass->whiten = gcSegWhiten; + klass->greyen = gcSegGreyen; + klass->blacken = gcSegTrivBlacken; + klass->scan = segNoScan; /* no useful default method */ + klass->fix = segNoFix; /* no useful default method */ + klass->fixEmergency = segNoFix; /* no useful default method */ + klass->reclaim = segNoReclaim; /* no useful default method */ + klass->walk = segTrivWalk; + AVERT(SegClass, klass); +} + + +/* MutatorSegClass -- collectable mutator segment class definition */ + +typedef SegClassStruct MutatorSegClassStruct; + +DEFINE_CLASS(Seg, MutatorSeg, klass) +{ + INHERIT_CLASS(klass, MutatorSeg, GCSeg); + klass->setSummary = mutatorSegSetSummary; + klass->setGrey = mutatorSegSetGrey; + klass->flip = mutatorSegFlip; + klass->setRankSet = mutatorSegSetRankSet; + klass->setRankSummary = mutatorSegSetRankSummary; + AVERT(SegClass, klass); } @@ -1691,17 +2198,17 @@ DEFINE_CLASS(GCSegClass, class) * may mix this in to ensure that erroneous calls are checked. */ -void SegClassMixInNoSplitMerge(SegClass class) +void SegClassMixInNoSplitMerge(SegClass klass) { /* Can't check class because it's not initialized yet */ - class->merge = segNoMerge; - class->split = segNoSplit; + klass->merge = segNoMerge; + klass->split = segNoSplit; } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2015 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/segsmss.c b/mps/code/segsmss.c index 73f3f79461b..96f60dd2798 100644 --- a/mps/code/segsmss.c +++ b/mps/code/segsmss.c @@ -26,12 +26,6 @@ #include /* fflush, printf, puts, stdout */ -/* Forward declarations */ - -extern SegClass AMSTSegClassGet(void); -extern PoolClass AMSTPoolClassGet(void); - - /* Start by defining the AMST pool (AMS Test pool) */ #define AMSTSig ((Sig)0x519A3529) /* SIGnature AMST */ @@ -56,6 +50,12 @@ typedef struct AMSTStruct *AMST; #define AMST2AMS(amst) (&(amst)->amsStruct) +typedef AMST AMSTPool; +#define AMSTPoolCheck AMSTCheck +DECLARE_CLASS(Pool, AMSTPool, AMSPool); +DECLARE_CLASS(Seg, AMSTSeg, AMSSeg); + + /* AMSTCheck -- the check method for an AMST */ ATTRIBUTE_UNUSED @@ -111,32 +111,29 @@ static Bool AMSTSegCheck(AMSTSeg amstseg) /* amstSegInit -- initialise an amst segment */ -static Res amstSegInit(Seg seg, Pool pool, Addr base, Size size, - Bool reservoirPermit, ArgList args) +static Res amstSegInit(Seg seg, Pool pool, Addr base, Size size, ArgList args) { - SegClass super; AMSTSeg amstseg; AMST amst; Res res; - AVERT(Seg, seg); - amstseg = Seg2AMSTSeg(seg); + /* Initialize the superclass fields first via next-method call */ + res = NextMethod(Seg, AMSTSeg, init)(seg, pool, base, size, args); + if (res != ResOK) + return res; + amstseg = CouldBeA(AMSTSeg, seg); + AVERT(Pool, pool); amst = PoolAMST(pool); AVERT(AMST, amst); /* no useful checks for base and size */ - AVERT(Bool, reservoirPermit); - - /* Initialize the superclass fields first via next-method call */ - super = SEG_SUPERCLASS(AMSTSegClass); - res = super->init(seg, pool, base, size, reservoirPermit, args); - if (res != ResOK) - return res; amstseg->next = NULL; amstseg->prev = NULL; + + SetClassOfPoly(seg, CLASS(AMSTSeg)); amstseg->sig = AMSTSegSig; - AVERT(AMSTSeg, amstseg); + AVERC(AMSTSeg, amstseg); return ResOK; } @@ -144,13 +141,11 @@ static Res amstSegInit(Seg seg, Pool pool, Addr base, Size size, /* amstSegFinish -- Finish method for AMST segments */ -static void amstSegFinish(Seg seg) +static void amstSegFinish(Inst inst) { - SegClass super; - AMSTSeg amstseg; + Seg seg = MustBeA(Seg, inst); + AMSTSeg amstseg = MustBeA(AMSTSeg, seg); - AVERT(Seg, seg); - amstseg = Seg2AMSTSeg(seg); AVERT(AMSTSeg, amstseg); if (amstseg->next != NULL) @@ -160,8 +155,7 @@ static void amstSegFinish(Seg seg) amstseg->sig = SigInvalid; /* finish the superclass fields last */ - super = SEG_SUPERCLASS(AMSTSegClass); - super->finish(seg); + NextMethod(Inst, AMSTSeg, finish)(inst); } @@ -176,10 +170,8 @@ static void amstSegFinish(Seg seg) * anti-method. */ static Res amstSegMerge(Seg seg, Seg segHi, - Addr base, Addr mid, Addr limit, - Bool withReservoirPermit) + Addr base, Addr mid, Addr limit) { - SegClass super; AMST amst; AMSTSeg amstseg, amstsegHi; Res res; @@ -193,9 +185,7 @@ static Res amstSegMerge(Seg seg, Seg segHi, amst = PoolAMST(SegPool(seg)); /* Merge the superclass fields via direct next-method call */ - super = SEG_SUPERCLASS(AMSTSegClass); - res = super->merge(seg, segHi, base, mid, limit, - withReservoirPermit); + res = NextMethod(Seg, AMSTSeg, merge)(seg, segHi, base, mid, limit); if (res != ResOK) goto failSuper; @@ -214,8 +204,7 @@ static Res amstSegMerge(Seg seg, Seg segHi, failDeliberate: /* Call the anti-method (see .fail) */ - res = super->split(seg, segHi, base, mid, limit, - withReservoirPermit); + res = NextMethod(Seg, AMSTSeg, split)(seg, segHi, base, mid, limit); AVER(res == ResOK); res = ResFAIL; failSuper: @@ -228,10 +217,8 @@ static Res amstSegMerge(Seg seg, Seg segHi, /* amstSegSplit -- AMSTSeg split method */ static Res amstSegSplit(Seg seg, Seg segHi, - Addr base, Addr mid, Addr limit, - Bool withReservoirPermit) + Addr base, Addr mid, Addr limit) { - SegClass super; AMST amst; AMSTSeg amstseg, amstsegHi; Res res; @@ -244,9 +231,7 @@ static Res amstSegSplit(Seg seg, Seg segHi, amst = PoolAMST(SegPool(seg)); /* Split the superclass fields via direct next-method call */ - super = SEG_SUPERCLASS(AMSTSegClass); - res = super->split(seg, segHi, base, mid, limit, - withReservoirPermit); + res = NextMethod(Seg, AMSTSeg, split)(seg, segHi, base, mid, limit); if (res != ResOK) goto failSuper; @@ -269,8 +254,7 @@ static Res amstSegSplit(Seg seg, Seg segHi, failDeliberate: /* Call the anti-method. (see .fail) */ - res = super->merge(seg, segHi, base, mid, limit, - withReservoirPermit); + res = NextMethod(Seg, AMSTSeg, merge)(seg, segHi, base, mid, limit); AVER(res == ResOK); res = ResFAIL; failSuper: @@ -281,16 +265,15 @@ static Res amstSegSplit(Seg seg, Seg segHi, /* AMSTSegClass -- Class definition for AMST segments */ -DEFINE_SEG_CLASS(AMSTSegClass, class) +DEFINE_CLASS(Seg, AMSTSeg, klass) { - INHERIT_CLASS(class, AMSSegClass); - class->name = "AMSTSEG"; - class->size = sizeof(AMSTSegStruct); - class->init = amstSegInit; - class->finish = amstSegFinish; - class->split = amstSegSplit; - class->merge = amstSegMerge; - AVERT(SegClass, class); + INHERIT_CLASS(klass, AMSTSeg, AMSSeg); + klass->instClassStruct.finish = amstSegFinish; + klass->size = sizeof(AMSTSegStruct); + klass->init = amstSegInit; + klass->split = amstSegSplit; + klass->merge = amstSegMerge; + AVERT(SegClass, klass); } @@ -328,34 +311,19 @@ static Res AMSTSegSizePolicy(Size *sizeReturn, /* AMSTInit -- the pool class initialization method */ -static Res AMSTInit(Pool pool, ArgList args) +static Res AMSTInit(Pool pool, Arena arena, PoolClass klass, ArgList args) { - AMST amst; AMS ams; - Format format; - Chain chain; + AMST amst; + AMS ams; Res res; - 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 = AMSInitInternal(PoolAMS(pool), format, chain, gen, FALSE); + res = NextMethod(Pool, AMSTPool, init)(pool, arena, klass, args); if (res != ResOK) return res; - amst = PoolAMST(pool); - ams = PoolAMS(pool); + + amst = CouldBeA(AMSTPool, pool); + ams = MustBeA(AMSPool, pool); + ams->segSize = AMSTSegSizePolicy; ams->segClass = AMSTSegClassGet; amst->failSegs = TRUE; @@ -365,22 +333,26 @@ static Res AMSTInit(Pool pool, ArgList args) amst->badMerges = 0; amst->bsplits = 0; amst->bmerges = 0; + + SetClassOfPoly(pool, CLASS(AMSTPool)); amst->sig = AMSTSig; - AVERT(AMST, amst); + AVERC(AMSTPool, amst); + return ResOK; } /* AMSTFinish -- the pool class finish method */ -static void AMSTFinish(Pool pool) +static void AMSTFinish(Inst inst) { - AMST amst; + Pool pool = MustBeA(AbstractPool, inst); + AMST amst = MustBeA(AMSTPool, pool); - AVERT(Pool, pool); - amst = PoolAMST(pool); AVERT(AMST, amst); + amst->sig = SigInvalid; + printf("\nDestroying pool, having performed:\n"); printf(" %"PRIuLONGEST" splits (S)\n", (ulongest_t)amst->splits); printf(" %"PRIuLONGEST" merges (M)\n", (ulongest_t)amst->merges); @@ -390,8 +362,7 @@ static void AMSTFinish(Pool pool) printf(" %"PRIuLONGEST" buffered splits (C)\n", (ulongest_t)amst->bsplits); printf(" %"PRIuLONGEST" buffered merges (J)\n", (ulongest_t)amst->bmerges); - AMSFinish(pool); - amst->sig = SigInvalid; + NextMethod(Inst, AMSTPool, finish)(inst); } @@ -410,23 +381,14 @@ static Bool AMSSegIsFree(Seg seg) static Bool AMSSegRegionIsFree(Seg seg, Addr base, Addr limit) { - AMSSeg amsseg; - AMS ams; - Count bgrain, lgrain; - Addr sbase; - - AVERT(Seg, seg); - amsseg = Seg2AMSSeg(seg); - sbase = SegBase(seg); - ams = PoolAMS(SegPool(seg)); - - bgrain = AMSGrains(ams, AddrOffset(sbase, base)); - lgrain = AMSGrains(ams, AddrOffset(sbase, limit)); + AMSSeg amsseg = MustBeA(AMSSeg, seg); + Index baseIndex = PoolIndexOfAddr(SegBase(seg), SegPool(seg), base); if (amsseg->allocTableInUse) { - return BTIsResRange(amsseg->allocTable, bgrain, lgrain); + Index limitIndex = PoolIndexOfAddr(SegBase(seg), SegPool(seg), limit); + return BTIsResRange(amsseg->allocTable, baseIndex, limitIndex); } else { - return amsseg->firstFree <= bgrain; + return amsseg->firstFree <= baseIndex; } } @@ -434,18 +396,19 @@ static Bool AMSSegRegionIsFree(Seg seg, Addr base, Addr limit) /* AMSUnallocateRange -- set a range to be unallocated * * Used as a means of overriding the behaviour of AMSBufferFill. - * The code is similar to AMSBufferEmpty. + * The code is similar to amsSegBufferEmpty. */ static void AMSUnallocateRange(AMS ams, Seg seg, Addr base, Addr limit) { AMSSeg amsseg; Index baseIndex, limitIndex; + Count unallocatedGrains; /* parameters checked by caller */ amsseg = Seg2AMSSeg(seg); - baseIndex = AMS_ADDR_INDEX(seg, base); - limitIndex = AMS_ADDR_INDEX(seg, limit); + baseIndex = PoolIndexOfAddr(SegBase(seg), SegPool(seg), base); + limitIndex = PoolIndexOfAddr(SegBase(seg), SegPool(seg), limit); if (amsseg->allocTableInUse) { /* check that it's allocated */ @@ -464,10 +427,14 @@ static void AMSUnallocateRange(AMS ams, Seg seg, Addr base, Addr limit) BTResRange(amsseg->allocTable, baseIndex, limitIndex); } } - amsseg->freeGrains += limitIndex - baseIndex; - AVER(amsseg->newGrains >= limitIndex - baseIndex); - amsseg->newGrains -= limitIndex - baseIndex; - PoolGenAccountForEmpty(&ams->pgen, AddrOffset(base, limit), FALSE); + + unallocatedGrains = limitIndex - baseIndex; + AVER(amsseg->bufferedGrains >= unallocatedGrains); + amsseg->freeGrains += unallocatedGrains; + amsseg->bufferedGrains -= unallocatedGrains; + PoolGenAccountForEmpty(ams->pgen, 0, + PoolGrainsSize(AMSPool(ams), unallocatedGrains), + FALSE); } @@ -480,12 +447,13 @@ static void AMSAllocateRange(AMS ams, Seg seg, Addr base, Addr limit) { AMSSeg amsseg; Index baseIndex, limitIndex; + Count allocatedGrains; /* parameters checked by caller */ amsseg = Seg2AMSSeg(seg); - baseIndex = AMS_ADDR_INDEX(seg, base); - limitIndex = AMS_ADDR_INDEX(seg, limit); + baseIndex = PoolIndexOfAddr(SegBase(seg), SegPool(seg), base); + limitIndex = PoolIndexOfAddr(SegBase(seg), SegPool(seg), limit); if (amsseg->allocTableInUse) { /* check that it's not allocated */ @@ -504,10 +472,12 @@ static void AMSAllocateRange(AMS ams, Seg seg, Addr base, Addr limit) BTSetRange(amsseg->allocTable, baseIndex, limitIndex); } } - AVER(amsseg->freeGrains >= limitIndex - baseIndex); - amsseg->freeGrains -= limitIndex - baseIndex; - amsseg->newGrains += limitIndex - baseIndex; - PoolGenAccountForFill(&ams->pgen, AddrOffset(base, limit), FALSE); + + allocatedGrains = limitIndex - baseIndex; + AVER(amsseg->freeGrains >= allocatedGrains); + amsseg->freeGrains -= allocatedGrains; + amsseg->bufferedGrains += allocatedGrains; + PoolGenAccountForFill(ams->pgen, AddrOffset(base, limit)); } @@ -516,20 +486,18 @@ static void AMSAllocateRange(AMS ams, Seg seg, Addr base, Addr limit) * Calls next method - but possibly splits or merges the chosen * segment. * - * .merge: A merge is performed when the next method returns - * the entire segment, this segment had previously been split - * from the segment below, and the segment below is appropriately - * similar (i.e. not already attached to a buffer and similarly grey) + * .merge: A merge is performed when the next method returns the + * entire segment, this segment had previously been split from the + * segment below, and the segment below is appropriately similar + * (i.e. not already attached to a buffer and similarly coloured) * * .split: If we're not merging, a split is performed if the next method * returns the entire segment, and yet lower half of the segment would * meet the request. */ static Res AMSTBufferFill(Addr *baseReturn, Addr *limitReturn, - Pool pool, Buffer buffer, Size size, - Bool withReservoirPermit) + Pool pool, Buffer buffer, Size size) { - PoolClass super; Addr base, limit; Arena arena; AMS ams; @@ -548,9 +516,7 @@ static Res AMSTBufferFill(Addr *baseReturn, Addr *limitReturn, amst = PoolAMST(pool); /* call next method */ - super = POOL_SUPERCLASS(AMSTPoolClass); - res = super->bufferFill(&base, &limit, pool, buffer, size, - withReservoirPermit); + res = NextMethod(Pool, AMSTPool, bufferFill)(&base, &limit, pool, buffer, size); if (res != ResOK) return res; @@ -561,13 +527,15 @@ static Res AMSTBufferFill(Addr *baseReturn, Addr *limitReturn, if (SegLimit(seg) == limit && SegBase(seg) == base) { if (amstseg->prev != NULL) { Seg segLo = AMSTSeg2Seg(amstseg->prev); - if (SegBuffer(segLo) == NULL && SegGrey(segLo) == SegGrey(seg)) { + if (!SegHasBuffer(segLo) && + SegGrey(segLo) == SegGrey(seg) && + SegWhite(segLo) == SegWhite(seg)) { /* .merge */ Seg mergedSeg; Res mres; AMSUnallocateRange(ams, seg, base, limit); - mres = SegMerge(&mergedSeg, segLo, seg, withReservoirPermit); + mres = SegMerge(&mergedSeg, segLo, seg); if (ResOK == mres) { /* successful merge */ AMSAllocateRange(ams, mergedSeg, base, limit); /* leave range as-is */ @@ -585,7 +553,7 @@ static Res AMSTBufferFill(Addr *baseReturn, Addr *limitReturn, Seg segLo, segHi; Res sres; AMSUnallocateRange(ams, seg, mid, limit); - sres = SegSplit(&segLo, &segHi, seg, mid, withReservoirPermit); + sres = SegSplit(&segLo, &segHi, seg, mid); if (ResOK == sres) { /* successful split */ limit = mid; /* range is lower segment */ } else { /* failed to split */ @@ -622,10 +590,11 @@ static void AMSTStressBufferedSeg(Seg seg, Buffer buffer) AMST amst; Arena arena; Addr limit; + Buffer segBuf; AVERT(Seg, seg); AVERT(Buffer, buffer); - AVER(SegBuffer(seg) == buffer); + AVER(SegBuffer(&segBuf, seg) && segBuf == buffer); amstseg = Seg2AMSTSeg(seg); AVERT(AMSTSeg, amstseg); limit = BufferLimit(buffer); @@ -639,7 +608,7 @@ static void AMSTStressBufferedSeg(Seg seg, Buffer buffer) /* .bmerge */ Seg mergedSeg; Res res; - res = SegMerge(&mergedSeg, seg, segHi, FALSE); + res = SegMerge(&mergedSeg, seg, segHi); if (ResOK == res) { amst->bmerges++; printf("J"); @@ -656,7 +625,7 @@ static void AMSTStressBufferedSeg(Seg seg, Buffer buffer) /* .bsplit */ Seg segLo, segHi; Res res; - res = SegSplit(&segLo, &segHi, seg, limit, FALSE); + res = SegSplit(&segLo, &segHi, seg, limit); if (ResOK == res) { amst->bsplits++; printf("C"); @@ -671,16 +640,14 @@ static void AMSTStressBufferedSeg(Seg seg, Buffer buffer) /* AMSTPoolClass -- the pool class definition */ -DEFINE_POOL_CLASS(AMSTPoolClass, this) +DEFINE_CLASS(Pool, AMSTPool, klass) { - INHERIT_CLASS(this, AMSPoolClass); - this->name = "AMST"; - this->size = sizeof(AMSTStruct); - this->offset = offsetof(AMSTStruct, amsStruct) + offsetof(AMSStruct, poolStruct); - this->init = AMSTInit; - this->finish = AMSTFinish; - this->bufferFill = AMSTBufferFill; - AVERT(PoolClass, this); + INHERIT_CLASS(klass, AMSTPool, AMSPool); + klass->instClassStruct.finish = AMSTFinish; + klass->size = sizeof(AMSTStruct); + klass->init = AMSTInit; + klass->bufferFill = AMSTBufferFill; + AVERT(PoolClass, klass); } @@ -704,7 +671,7 @@ static void mps_amst_ap_stress(mps_ap_t ap) static mps_pool_class_t mps_class_amst(void) { - return (mps_pool_class_t)AMSTPoolClassGet(); + return (mps_pool_class_t)CLASS(AMSTPool); } diff --git a/mps/code/shield.c b/mps/code/shield.c index 0dfc1945db6..c50218fda3f 100644 --- a/mps/code/shield.c +++ b/mps/code/shield.c @@ -1,75 +1,13 @@ /* shield.c: SHIELD IMPLEMENTATION * * $Id$ - * Copyright (c) 2001-2015 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * * See: idea.shield, design.mps.shield. * - * This implementation of the shield avoids suspending threads for - * as long as possible. When threads are suspended, it maintains a - * cache of covered segments where the desired and actual protection - * do not match. This cache is flushed on leaving the shield. - * - * - * Definitions - * - * .def.synced: a seg is synced if the prot and shield modes are the - * same, and unsynced otherwise. - * .def.depth: the depth of a segment is defined as - * depth == #exposes - #covers + #(in cache), where - * #exposes = the total number of times the seg has been exposed - * #covers = the total number of times the seg has been covered - * #(in cache) = the number of times the seg appears in the cache - * The cache is initially empty and Cover should not be called - * without a matching Expose, so this figure should always be - * non-negative. - * .def.total.depth: The total depth is the sum of the depth over - * all segments - * .def.outside: being outside the shield is being between calls - * to leave and enter, and similarly .def.inside: being inside the - * shield is being between calls to enter and leave. - * .def.suspended: suspended is true iff the mutator is suspended. - * .def.shielded: a segment is shielded if the shield mode is non-zero. - * - * - * Properties - * - * .prop.outside.running: The mutator may not be suspended while - * outside the shield. - * .prop.mutator.access: An attempt by the mutator to access - * shielded memory must cause an ArenaAccess. - * .prop.inside.access: Inside the shield it must be possible to access - * all unshielded segments and all exposed segments. - * - * - * Invariants - * - * These invariants are maintained by the code. - * - * .inv.outside.running: The mutator is not suspended while outside the - * shield. - * .inv.unsynced.suspended: If any segment is not synced, - * the mutator is suspended. - * .inv.unsynced.depth: All unsynced segments have positive depth. - * .inv.outside.depth: The total depth is zero while outside the shield. - * .inv.prot.shield: The prot mode is never more than the shield mode. - * .inv.expose.prot: An exposed seg is not protected. - * - * Hints at proofs of properties from invariants - * - * inv.outside.running directly ensures prop.outside running. - * - * As the depth of a segment cannot be negative - * total depth == 0 => for all segments, depth == 0 - * => all segs are synced (by .inv.unsynced.depth) - * - * If the mutator is running then all segs must be synced - * (.inv.unsynced.suspend). Which means that the hardware protection - * (prot mode) must reflect the software protection (shield mode). - * Hence all shielded memory will be hardware protected while the - * mutator is running. This ensures .prop.mutator.access. - * - * inv.prot.shield and inv.expose.prot ensure prop.inside.access. + * IMPORTANT: HERE BE DRAGONS! This code is subtle and + * critical. Ensure you have read and understood design.mps.shield + * before you touch it. */ #include "mpm.h" @@ -77,269 +15,754 @@ SRCID(shield, "$Id$"); -void (ShieldSuspend)(Arena arena) +void ShieldInit(Shield shield) { - AVERT(Arena, arena); - AVER(arena->insideShield); + shield->inside = FALSE; + shield->suspended = FALSE; + shield->queuePending = FALSE; + shield->queue = NULL; + shield->length = 0; + shield->next = 0; + shield->limit = 0; + shield->depth = 0; + shield->unsynced = 0; + shield->holds = 0; + shield->sig = ShieldSig; +} - if (!arena->suspended) { - ThreadRingSuspend(ArenaThreadRing(arena), ArenaDeadRing(arena)); - arena->suspended = TRUE; + +void ShieldDestroyQueue(Shield shield, Arena arena) +{ + AVER(shield->limit == 0); /* queue must be empty */ + + if (shield->length != 0) { + AVER(shield->queue != NULL); + ControlFree(arena, shield->queue, + shield->length * sizeof shield->queue[0]); + shield->queue = NULL; + shield->length = 0; } } -void (ShieldResume)(Arena arena) +void ShieldFinish(Shield shield) { - AVERT(Arena, arena); - AVER(arena->insideShield); - AVER(arena->suspended); - /* It is only correct to actually resume the mutator here if shDepth is 0 */ + /* The queue should already have been destroyed by + GlobalsPrepareToDestroy calling ShieldDestroyQueue. */ + AVER(shield->length == 0); + AVER(shield->limit == 0); + AVER(shield->queue == NULL); + + AVER(shield->depth == 0); + AVER(shield->unsynced == 0); + AVER(shield->holds == 0); + shield->sig = SigInvalid; } -/* This ensures actual prot mode does not include mode */ -static void protLower(Arena arena, Seg seg, AccessSet mode) -{ - /* */ - AVERT_CRITICAL(Arena, arena); - UNUSED(arena); - AVERT_CRITICAL(Seg, seg); - AVERT_CRITICAL(AccessSet, mode); +static Bool SegIsSynced(Seg seg); - if (SegPM(seg) & mode) { - SegSetPM(seg, SegPM(seg) & ~mode); +Bool ShieldCheck(Shield shield) +{ + CHECKS(Shield, shield); + /* Can't check Boolean bitfields */ + CHECKL(shield->queue == NULL || shield->length > 0); + CHECKL(shield->limit <= shield->length); + CHECKL(shield->next <= shield->limit); + + /* The mutator is not suspended while outside the shield + (design.mps.shield.inv.outside.running). */ + CHECKL(shield->inside || !shield->suspended); + + /* If any segment is not synced, the mutator is suspended + (design.mps.shield.inv.unsynced.suspended). */ + CHECKL(shield->unsynced == 0 || shield->suspended); + + /* The total depth is zero while outside the shield + (design.mps.shield.inv.outside.depth). */ + CHECKL(shield->inside || shield->depth == 0); + + /* There are no unsynced segments when we're outside the shield. */ + CHECKL(shield->inside || shield->unsynced == 0); + + /* Every unsynced segment should be on the queue, because we have to + remember to sync it before we return to the mutator. */ + CHECKL(shield->limit + shield->queuePending >= shield->unsynced); + + /* The mutator is suspeneded if there are any holds. */ + CHECKL(shield->holds == 0 || shield->suspended); + + /* This is too expensive to check all the time since we have an + expanding shield queue that often has 16K elements instead of + 16. */ +#if defined(AVER_AND_CHECK_ALL) + { + Count unsynced = 0; + Index i; + for (i = 0; i < shield->limit; ++i) { + Seg seg = shield->queue[i]; + CHECKD(Seg, seg); + if (!SegIsSynced(seg)) + ++unsynced; + } + CHECKL(unsynced + shield->queuePending == shield->unsynced); + } +#endif + + return TRUE; +} + + +Res ShieldDescribe(Shield shield, mps_lib_FILE *stream, Count depth) +{ + Res res; + + res = WriteF(stream, depth, + "Shield $P {\n", (WriteFP)shield, + " ", shield->inside ? "inside" : "outside", " shield\n", + " suspended $S\n", WriteFYesNo(shield->suspended), + " depth $U\n", (WriteFU)shield->depth, + " next $U\n", (WriteFU)shield->next, + " length $U\n", (WriteFU)shield->length, + " unsynced $U\n", (WriteFU)shield->unsynced, + " holds $U\n", (WriteFU)shield->holds, + "} Shield $P\n", (WriteFP)shield, + NULL); + if (res != ResOK) + return res; + + return ResOK; +} + + +/* SHIELD_AVER -- transgressive argument checking + * + * .trans.check: A number of shield functions cannot do normal + * argument checking with AVERT because (for example) SegCheck checks + * the shield invariants, and it is these functions that are enforcing + * them. Instead, we AVER(TESTT(Seg, seg)) to check the type + * signature but not the contents. + */ + +#define SHIELD_AVERT(type, exp) AVER(TESTT(type, exp)) +#define SHIELD_AVERT_CRITICAL(type, exp) AVER_CRITICAL(TESTT(type, exp)) + + +/* SegIsSynced -- is a segment synced? + * + * See design.mps.shield.def.synced. + */ + +static Bool SegIsSynced(Seg seg) +{ + SHIELD_AVERT_CRITICAL(Seg, seg); + return SegSM(seg) == SegPM(seg); +} + + +/* shieldSetSM -- set shield mode, maintaining sync count */ + +static void shieldSetSM(Shield shield, Seg seg, AccessSet mode) +{ + if (SegSM(seg) != mode) { + if (SegIsSynced(seg)) { + SegSetSM(seg, mode); + ++shield->unsynced; + } else { + SegSetSM(seg, mode); + if (SegIsSynced(seg)) { + AVER(shield->unsynced > 0); + --shield->unsynced; + } + } + } +} + + +/* shieldSetPM -- set protection mode, maintaining sync count */ + +static void shieldSetPM(Shield shield, Seg seg, AccessSet mode) +{ + if (SegPM(seg) != mode) { + if (SegIsSynced(seg)) { + SegSetPM(seg, mode); + ++shield->unsynced; + } else { + SegSetPM(seg, mode); + if (SegIsSynced(seg)) { + AVER(shield->unsynced > 0); + --shield->unsynced; + } + } + } +} + + +/* SegIsExposed -- is a segment exposed? + * + * See design.mps.shield.def.exposed. + */ + +static Bool SegIsExposed(Seg seg) +{ + SHIELD_AVERT_CRITICAL(Seg, seg); + return seg->depth > 0; +} + + +/* shieldSync -- synchronize a segment's protection + * + * See design.mps.shield.inv.prot.shield. + */ + +static void shieldSync(Shield shield, Seg seg) +{ + SHIELD_AVERT_CRITICAL(Seg, seg); + + if (!SegIsSynced(seg)) { + shieldSetPM(shield, seg, SegSM(seg)); ProtSet(SegBase(seg), SegLimit(seg), SegPM(seg)); } } -static void shieldSync(Arena arena, Seg seg) -{ - AVERT(Arena, arena); - AVERT(Seg, seg); +/* shieldSuspend -- suspend the mutator + * + * Called from inside impl.c.shield when any segment is not synced, in + * order to provide exclusive access to the segment by the MPS. See + * .inv.unsynced.suspended. + */ - if (SegPM(seg) != SegSM(seg)) { - ProtSet(SegBase(seg), SegLimit(seg), SegSM(seg)); - SegSetPM(seg, SegSM(seg)); - /* inv.prot.shield */ +static void shieldSuspend(Arena arena) +{ + Shield shield; + + AVERT(Arena, arena); + shield = ArenaShield(arena); + AVER(shield->inside); + + if (!shield->suspended) { + ThreadRingSuspend(ArenaThreadRing(arena), ArenaDeadRing(arena)); + shield->suspended = TRUE; } } -static void flush(Arena arena, Size i) +/* ShieldHold -- suspend mutator access to the unprotectable + * + * From outside impl.c.shield, this is used when we really need to + * lock everything against the mutator -- for example, during flip + * when we must scan all thread registers at once. + */ + +void (ShieldHold)(Arena arena) { - Seg seg; AVERT(Arena, arena); - AVER(i < arena->shCacheLimit); - - seg = arena->shCache[i]; - if (seg == NULL) - return; - AVERT(Seg, seg); - - AVER(arena->shDepth > 0); - AVER(SegDepth(seg) > 0); - --arena->shDepth; - SegSetDepth(seg, SegDepth(seg) - 1); - - if (SegDepth(seg) == 0) - shieldSync(arena, seg); - - arena->shCache[i] = NULL; + shieldSuspend(arena); + ++ArenaShield(arena)->holds; } -/* If the segment is out of sync, either sync it, or ensure - * depth > 0, and the arena is suspended. +/* ShieldRelease -- declare mutator could be resumed + * + * In practice, we don't resume the mutator until ShieldLeave, but + * this marks the earliest point at which we could resume. */ -static void cache(Arena arena, Seg seg) + +void (ShieldRelease)(Arena arena) +{ + Shield shield; + + AVERT(Arena, arena); + shield = ArenaShield(arena); + AVER(shield->inside); + AVER(shield->suspended); + + AVER(shield->holds > 0); + --shield->holds; + + /* It is only correct to actually resume the mutator here if + shield->depth is 0, shield->unsycned is 0, and the queue is + empty. */ + /* See design.mps.shield.improv.resume for a discussion of when it + might be a good idea to resume the mutator early. */ +} + + +/* shieldProtLower -- reduce protection on a segment + * + * This ensures actual prot mode does not include mode. + */ + +static void shieldProtLower(Shield shield, Seg seg, AccessSet mode) { /* */ - AVERT_CRITICAL(Arena, arena); - AVERT_CRITICAL(Seg, seg); + SHIELD_AVERT_CRITICAL(Seg, seg); + AVERT_CRITICAL(AccessSet, mode); - if (SegSM(seg) - == SegPM(seg)) return; - if (SegDepth(seg) > 0) { - ShieldSuspend(arena); - return; - } - if (ShieldCacheSIZE == 0 || !arena->suspended) - shieldSync(arena, seg); - else { - SegSetDepth(seg, SegDepth(seg) + 1); - ++arena->shDepth; - AVER(arena->shDepth > 0); - AVER(SegDepth(seg) > 0); - AVER(arena->shCacheLimit <= ShieldCacheSIZE); - AVER(arena->shCacheI < arena->shCacheLimit); - flush(arena, arena->shCacheI); - arena->shCache[arena->shCacheI] = seg; - ++arena->shCacheI; - if (arena->shCacheI == ShieldCacheSIZE) - arena->shCacheI = 0; - if (arena->shCacheI == arena->shCacheLimit) - ++arena->shCacheLimit; + if (BS_INTER(SegPM(seg), mode) != AccessSetEMPTY) { + shieldSetPM(shield, seg, BS_DIFF(SegPM(seg), mode)); + ProtSet(SegBase(seg), SegLimit(seg), SegPM(seg)); } } -void (ShieldRaise) (Arena arena, Seg seg, AccessSet mode) +/* shieldDequeue -- remove a segment from the shield queue */ + +static Seg shieldDequeue(Shield shield, Index i) { - /* .seg.broken: Seg's shield invariants may not be true at */ - /* this point (this function is called to enforce them) so we */ - /* can't check seg. Nor can we check arena as that checks the */ - /* segs in the cache. */ + Seg seg; + AVER(i < shield->limit); + seg = shield->queue[i]; + AVERT(Seg, seg); + AVER(seg->queued); + shield->queue[i] = NULL; /* to ensure it can't get re-used */ + seg->queued = FALSE; + return seg; +} + +/* shieldFlushEntry -- flush a single entry from the queue + * + * If the segment is exposed we can simply dequeue it, because later + * there will be a call to ShieldCover that will put it back on the + * queue. If the segment is not exposed, we can sync its protection. + * (And if it does not have the shield raised any more, that will do + * nothing.) + */ + +static void shieldFlushEntry(Shield shield, Index i) +{ + Seg seg = shieldDequeue(shield, i); + + if (!SegIsExposed(seg)) + shieldSync(shield, seg); +} + + +/* shieldQueueReset -- reset shield queue pointers */ + +static void shieldQueueReset(Shield shield) +{ + AVER(shield->depth == 0); /* overkill: implies no segs are queued */ + AVER(shield->unsynced == 0); + shield->next = 0; + shield->limit = 0; +} + + +/* shieldQueueEntryCompare -- comparison for queue sorting */ + +static Compare shieldAddrCompare(Addr left, Addr right) +{ + if (left < right) + return CompareLESS; + else if (left == right) + return CompareEQUAL; + else + return CompareGREATER; +} + +static Compare shieldQueueEntryCompare(void *left, void *right, void *closure) +{ + Seg segA = left, segB = right; + + /* These checks are not critical in a hot build, but slow down cool + builds quite a bit, so just check the signatures. */ + AVER(TESTT(Seg, segA)); + AVER(TESTT(Seg, segB)); + UNUSED(closure); + + return shieldAddrCompare(SegBase(segA), SegBase(segB)); +} + + +/* shieldFlushEntries -- flush queue coalescing protects + * + * Sort the shield queue into address order, then iterate over it + * coalescing protection work, in order to reduce the number of system + * calls to a minimum. This is very important on macOS, where + * protection calls are extremely inefficient, but has no net gain on + * Windows. + * + * TODO: Could we keep extending the outstanding area over memory + * that's *not* in the queue but has the same protection mode? Might + * require design.mps.shield.improve.noseg. + */ + +static void shieldFlushEntries(Shield shield) +{ + Addr base = NULL, limit; + AccessSet mode; + Index i; + + if (shield->length == 0) { + AVER(shield->queue == NULL); + return; + } + + QuickSort((void *)shield->queue, shield->limit, + shieldQueueEntryCompare, UNUSED_POINTER, + &shield->sortStruct); + + mode = AccessSetEMPTY; + limit = NULL; + for (i = 0; i < shield->limit; ++i) { + Seg seg = shieldDequeue(shield, i); + if (!SegIsSynced(seg)) { + shieldSetPM(shield, seg, SegSM(seg)); + if (SegSM(seg) != mode || SegBase(seg) != limit) { + if (base != NULL) { + AVER(base < limit); + ProtSet(base, limit, mode); + } + base = SegBase(seg); + mode = SegSM(seg); + } + limit = SegLimit(seg); + } + } + if (base != NULL) { + AVER(base < limit); + ProtSet(base, limit, mode); + } + + shieldQueueReset(shield); +} + + +/* shieldQueue -- consider adding a segment to the queue + * + * If the segment is out of sync, either sync it, or ensure it is + * queued and the mutator is suspended. + */ + +static void shieldQueue(Arena arena, Seg seg) +{ + Shield shield; + + /* */ + AVERT_CRITICAL(Arena, arena); + shield = ArenaShield(arena); + SHIELD_AVERT_CRITICAL(Seg, seg); + + if (SegIsSynced(seg) || seg->queued) + return; + + if (SegIsExposed(seg)) { + /* This can occur if the mutator isn't suspended, we expose a + segment, then raise the shield on it. In this case, the + mutator isn't allowed to see the segment, but we don't need to + queue it until its covered. */ + shieldSuspend(arena); + return; + } + + /* Allocate or extend the shield queue if necessary. */ + if (shield->next >= shield->length) { + void *p; + Res res; + Count length; + + AVER(shield->next == shield->length); + + if (shield->length == 0) + length = ShieldQueueLENGTH; + else + length = shield->length * 2; + + res = ControlAlloc(&p, arena, length * sizeof shield->queue[0]); + if (res != ResOK) { + AVER(ResIsAllocFailure(res)); + /* Carry on with the existing queue. */ + } else { + if (shield->length > 0) { + Size oldSize = shield->length * sizeof shield->queue[0]; + AVER(shield->queue != NULL); + mps_lib_memcpy(p, shield->queue, oldSize); + ControlFree(arena, shield->queue, oldSize); + } + shield->queue = p; + shield->length = length; + } + } + + /* Queue unavailable, so synchronize now. Or if the mutator is not + yet suspended and the code raises the shield on a covered + segment, protect it now, because that's probably better than + suspending the mutator. */ + if (shield->length == 0 || !shield->suspended) { + shieldSync(shield, seg); + return; + } + + AVER_CRITICAL(shield->limit <= shield->length); + AVER_CRITICAL(shield->next <= shield->limit); + + /* If we failed to extend the shield queue array, degrade to an LRU + circular buffer. */ + if (shield->next >= shield->length) + shield->next = 0; + AVER_CRITICAL(shield->next < shield->length); + + AVER_CRITICAL(shield->length > 0); + + /* If the limit is less than the length, then the queue array has + yet to be filled, and next is an uninitialized entry. + Otherwise it's the tail end from last time around, and needs to + be flushed. */ + if (shield->limit >= shield->length) { + AVER_CRITICAL(shield->limit == shield->length); + shieldFlushEntry(shield, shield->next); + } + + shield->queue[shield->next] = seg; + ++shield->next; + seg->queued = TRUE; + + if (shield->next >= shield->limit) + shield->limit = shield->next; +} + + +/* ShieldRaise -- declare segment should be protected from mutator + * + * Does not immediately protect the segment, unless the segment is + * covered and the shield queue is unavailable. + */ + +void (ShieldRaise)(Arena arena, Seg seg, AccessSet mode) +{ + Shield shield; + + SHIELD_AVERT(Arena, arena); + SHIELD_AVERT(Seg, seg); AVERT(AccessSet, mode); - AVER((SegSM(seg) & mode) == AccessSetEMPTY); - SegSetSM(seg, SegSM(seg) | mode); /* inv.prot.shield preserved */ + shield = ArenaShield(arena); + AVER(!shield->queuePending); + shield->queuePending = TRUE; - /* ensure inv.unsynced.suspended & inv.unsynced.depth */ - cache(arena, seg); + /* design.mps.shield.inv.prot.shield preserved */ + shieldSetSM(ArenaShield(arena), seg, BS_UNION(SegSM(seg), mode)); + + /* Ensure design.mps.shield.inv.unsynced.suspended and + design.mps.shield.inv.unsynced.depth */ + shieldQueue(arena, seg); + shield->queuePending = FALSE; + + /* Check queue and segment consistency. */ AVERT(Arena, arena); AVERT(Seg, seg); } +/* ShieldLower -- declare segment may be accessed by mutator */ + void (ShieldLower)(Arena arena, Seg seg, AccessSet mode) { - /* Don't check seg or arena, see .seg.broken */ + Shield shield; + + AVERT(Arena, arena); + shield = ArenaShield(arena); + SHIELD_AVERT(Seg, seg); AVERT(AccessSet, mode); - AVER((SegSM(seg) & mode) == mode); - /* synced(seg) is not changed by the following - * preserving inv.unsynced.suspended - * Also inv.prot.shield preserved - */ - SegSetSM(seg, SegSM(seg) & ~mode); - protLower(arena, seg, mode); + + /* SegIsSynced(seg) is not changed by the following preserving + design.mps.shield.inv.unsynced.suspended and + design.mps.shield.inv.prot.shield. */ + shieldSetSM(shield, seg, BS_DIFF(SegSM(seg), mode)); + /* TODO: Do we need to promptly call shieldProtLower here? It + loses the opportunity to coalesce the protection call. It would + violate design.mps.shield.prop.inside.access. */ + /* shieldQueue(arena, seg); */ + shieldProtLower(shield, seg, mode); + + /* Check queue and segment consistency. */ AVERT(Arena, arena); AVERT(Seg, seg); } +/* ShieldEnter -- enter the shield, allowing exposes */ + void (ShieldEnter)(Arena arena) { - Size i; + Shield shield; + + AVERT(Arena, arena); + shield = ArenaShield(arena); + AVER(!shield->inside); + AVER(shield->depth == 0); + AVER(!shield->suspended); + + shieldQueueReset(shield); + + shield->inside = TRUE; +} + + +/* shieldDebugCheck -- expensive consistency check + * + * While developing the shield it is very easy to make a consistency + * mistake that causes random corruption of the heap, usually because + * all the attempts to avoid protection and suspension end up failing + * to enforce design.mps.shield.prop.mutator.access. In these cases, + * try enabling SHIELD_DEBUG and extending this code as necessary. + * + * The basic idea is to iterate over *all* segments and check + * consistency with the arena and shield queue. + */ + +#if defined(SHIELD_DEBUG) +static void shieldDebugCheck(Arena arena) +{ + Shield shield; + Seg seg; + Count queued = 0; + Count depth = 0; AVERT(Arena, arena); - AVER(!arena->insideShield); - AVER(arena->shDepth == 0); - AVER(!arena->suspended); - AVER(arena->shCacheLimit <= ShieldCacheSIZE); - AVER(arena->shCacheI < arena->shCacheLimit); - for(i = 0; i < arena->shCacheLimit; i++) - AVER(arena->shCache[i] == NULL); + shield = ArenaShield(arena); + AVER(shield->inside || shield->limit == 0); - arena->shCacheI = (Size)0; - arena->shCacheLimit = (Size)1; - arena->insideShield = TRUE; + if (SegFirst(&seg, arena)) + do { + depth += SegDepth(seg); + if (shield->limit == 0) { + AVER(!seg->queued); + AVER(SegIsSynced(seg)); + /* You can directly set protections here to see if it makes a + difference. */ + /* ProtSet(SegBase(seg), SegLimit(seg), SegPM(seg)); */ + } else { + if (seg->queued) + ++queued; + } + } while(SegNext(&seg, arena, seg)); + + AVER(depth == shield->depth); + AVER(queued == shield->limit); } +#endif -/* .shield.flush: Flush empties the shield cache. - * This needs to be called before segments are destroyed as there - * may be references to them in the cache. +/* ShieldFlush -- empty the shield queue + * + * .shield.flush: Flush empties the shield queue. This needs to be + * called before queued segments are destroyed, to remove them from + * the queue. We flush the whole queue because finding the entry is + * O(n) and we're very likely reclaiming and destroying loads of + * segments. See also design.mps.shield.improv.resume. + * + * The memory for the segment may become spare, and not released back + * to the operating system. Since we keep track of protection on + * segments and not grains we have no way of keeping track of the + * protection state of spare grains. We therefore flush the protection + * to get it back into the default state (unprotected). See also + * design.mps.shield.improv.noseg. */ + void (ShieldFlush)(Arena arena) { - Size i; - - for(i = 0; i < arena->shCacheLimit; ++i) { - if (arena->shDepth == 0) - break; - flush(arena, i); - } + Shield shield; + + AVERT(Arena, arena); + shield = ArenaShield(arena); +#ifdef SHIELD_DEBUG + shieldDebugCheck(arena); +#endif + shieldFlushEntries(shield); + AVER(shield->unsynced == 0); /* everything back in sync */ +#ifdef SHIELD_DEBUG + shieldDebugCheck(arena); +#endif } +/* ShieldLeave -- leave the shield, protect segs from mutator */ + void (ShieldLeave)(Arena arena) { + Shield shield; + AVERT(Arena, arena); - AVER(arena->insideShield); + shield = ArenaShield(arena); + AVER(shield->inside); + AVER(shield->depth == 0); /* no pending covers */ + AVER(shield->holds == 0); ShieldFlush(arena); - /* Cache is empty so inv.outside.depth holds */ - AVER(arena->shDepth == 0); - /* Ensuring the mutator is running at this point - * guarantees inv.outside.running */ - if (arena->suspended) { + AVER(shield->unsynced == 0); /* everything back in sync */ + + /* Ensuring the mutator is running at this point guarantees + .inv.outside.running */ + if (shield->suspended) { ThreadRingResume(ArenaThreadRing(arena), ArenaDeadRing(arena)); - arena->suspended = FALSE; + shield->suspended = FALSE; } - arena->insideShield = FALSE; + + shield->inside = FALSE; } + /* ShieldExpose -- allow the MPS access to a segment while denying the mutator * - * The MPS currently does not collect concurrently, however the only thing - * that makes it not-concurrent is a critical point in the Shield - * abstraction where the MPS seeks to gain privileged access to memory - * (usually in order to scan it for GC). The critical point is where - * ShieldExpose in shield.c has to call ShieldSuspend to preserve the - * shield invariants. This is the only point in the MPS that prevents - * concurrency, and the rest of the MPS is designed to support it. - * - * The restriction could be removed if either: - * - * * the MPS could use a different set of protections to the mutator - * program - * - * * the mutator program uses a software barrier - * - * The first one is tricky, and the second one just hasn't come up in any - * implementation we've been asked to make yet. Given a VM, it could - * happen, and the MPS would be concurrent. - * - * So, I believe there's nothing fundamentally non-concurrent about the - * MPS design. It's kind of waiting to happen. - * - * (Originally written at .) + * The first expose of a shielded segment suspends the mutator to + * ensure the MPS has exclusive access. */ void (ShieldExpose)(Arena arena, Seg seg) { + Shield shield; AccessSet mode = AccessREAD | AccessWRITE; + /* */ AVERT_CRITICAL(Arena, arena); - AVER_CRITICAL(arena->insideShield); + shield = ArenaShield(arena); + AVER_CRITICAL(shield->inside); SegSetDepth(seg, SegDepth(seg) + 1); - ++arena->shDepth; - /* */ - AVER_CRITICAL(arena->shDepth > 0); - AVER_CRITICAL(SegDepth(seg) > 0); - if (SegPM(seg) & mode) - ShieldSuspend(arena); + AVER_CRITICAL(SegDepth(seg) > 0); /* overflow */ + ++shield->depth; + AVER_CRITICAL(shield->depth > 0); /* overflow */ + + if (BS_INTER(SegPM(seg), mode) != AccessSetEMPTY) + shieldSuspend(arena); - /* This ensures inv.expose.prot */ - protLower(arena, seg, mode); + /* Ensure design.mps.shield.inv.expose.prot. */ + /* TODO: Mass exposure -- see + design.mps.shield.improv.mass-expose. */ + shieldProtLower(shield, seg, mode); } +/* ShieldCover -- declare MPS no longer needs access to seg */ + void (ShieldCover)(Arena arena, Seg seg) { + Shield shield; + /* */ AVERT_CRITICAL(Arena, arena); + shield = ArenaShield(arena); AVERT_CRITICAL(Seg, seg); AVER_CRITICAL(SegPM(seg) == AccessSetEMPTY); - - AVER_CRITICAL(arena->shDepth > 0); + AVER_CRITICAL(SegDepth(seg) > 0); SegSetDepth(seg, SegDepth(seg) - 1); - --arena->shDepth; + AVER_CRITICAL(shield->depth > 0); + --shield->depth; - /* ensure inv.unsynced.depth */ - cache(arena, seg); + /* Ensure design.mps.shield.inv.unsynced.depth. */ + shieldQueue(arena, seg); } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2015 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/sncss.c b/mps/code/sncss.c new file mode 100644 index 00000000000..8756baf2b19 --- /dev/null +++ b/mps/code/sncss.c @@ -0,0 +1,237 @@ +/* sncss.c: SNC STRESS TEST + * + * $Id$ + * Copyright (c) 2014-2016 Ravenbrook Limited. See end of file for license. + */ + +#include "mpm.h" +#include "mpscmvt.h" +#include "mpscmvff.h" +#include "mpscsnc.h" +#include "mpsavm.h" +#include "mps.h" +#include "testlib.h" + +#include /* printf */ + + +/* Simple format for the SNC pool. */ + +typedef struct obj_s { + size_t size; + int pad; +} obj_s, *obj_t; + +/* make -- allocate one object, and if it's big enough, store the size + * in the first word, for the benefit of the object format */ + +static mps_res_t make(mps_addr_t *p, mps_ap_t ap, size_t size) +{ + mps_addr_t addr; + mps_res_t res; + + do { + obj_t obj; + res = mps_reserve(&addr, ap, size); + if (res != MPS_RES_OK) + return res; + obj = addr; + obj->size = size; + obj->pad = 0; + } while (!mps_commit(ap, addr, size)); + + *p = addr; + return MPS_RES_OK; +} + +static mps_res_t fmtScan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) +{ + testlib_unused(ss); + testlib_unused(base); + testlib_unused(limit); + return MPS_RES_OK; +} + +static mps_addr_t fmtSkip(mps_addr_t addr) +{ + obj_t obj = addr; + return (char *)addr + obj->size; +} + +static void fmtPad(mps_addr_t addr, size_t size) +{ + obj_t obj = addr; + obj->size = size; + obj->pad = 1; +} + +typedef struct env_s { + size_t obj; + size_t pad; +} env_s, *env_t; + +static void fmtVisitor(mps_addr_t object, mps_fmt_t format, + mps_pool_t pool, void *p, size_t s) +{ + env_t env = p; + obj_t obj = object; + testlib_unused(format); + testlib_unused(pool); + testlib_unused(s); + if (obj->pad) + env->pad += obj->size; + else + env->obj += obj->size; +} + +#define AP_MAX 3 /* Number of allocation points */ +#define DEPTH_MAX 20 /* Maximum depth of frame push */ + +typedef struct ap_s { + mps_ap_t ap; /* An allocation point on an ANC pool */ + size_t depth; /* Number of frames pushed */ + size_t alloc[DEPTH_MAX + 1]; /* Total allocation at each depth */ + size_t push[DEPTH_MAX]; /* Total allocation when we pushed */ + mps_frame_t frame[DEPTH_MAX]; /* The frame pointers at each depth */ +} ap_s, *ap_t; + +static void test(mps_pool_class_t pool_class) +{ + size_t i, j; + mps_align_t align; + mps_arena_t arena; + mps_fmt_t fmt; + mps_pool_t pool; + ap_s aps[AP_MAX]; + + align = sizeof(obj_s) << (rnd() % 4); + + 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_ALIGN, align); + MPS_ARGS_ADD(args, MPS_KEY_FMT_SCAN, fmtScan); + MPS_ARGS_ADD(args, MPS_KEY_FMT_SKIP, fmtSkip); + MPS_ARGS_ADD(args, MPS_KEY_FMT_PAD, fmtPad); + die(mps_fmt_create_k(&fmt, arena, args), "fmt_create"); + } MPS_ARGS_END(args); + + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_FORMAT, fmt); + die(mps_pool_create_k(&pool, arena, pool_class, args), "pool_create"); + } MPS_ARGS_END(args); + + for (i = 0; i < NELEMS(aps); ++i) { + ap_t a = &aps[i]; + die(mps_ap_create_k(&a->ap, pool, mps_args_none), "ap_create"); + a->depth = 0; + a->alloc[0] = 0; + } + + for (i = 0; i < 1000000; ++i) { + size_t k = rnd() % NELEMS(aps); + ap_t a = &aps[k]; + if (rnd() % 10 == 0) { + j = rnd() % NELEMS(a->frame); + if (j < a->depth) { + a->depth = j; + mps_ap_frame_pop(a->ap, a->frame[j]); + a->alloc[j] = a->push[j]; + } else { + a->push[a->depth] = a->alloc[a->depth]; + mps_ap_frame_push(&a->frame[a->depth], a->ap); + ++ a->depth; + a->alloc[a->depth] = 0; + } + } else { + size_t size = alignUp(1 + rnd() % 128, align); + mps_addr_t p; + make(&p, a->ap, size); + a->alloc[a->depth] += size; + } + } + + { + env_s env = {0, 0}; + size_t alloc = 0; + size_t free = mps_pool_free_size(pool); + size_t total = mps_pool_total_size(pool); + + for (i = 0; i < NELEMS(aps); ++i) { + ap_t a = &aps[i]; + for (j = 0; j <= a->depth; ++j) { + alloc += a->alloc[j]; + } + } + + mps_arena_formatted_objects_walk(arena, fmtVisitor, &env, 0); + + printf("alloc=%lu obj=%lu pad=%lu free=%lu total=%lu\n", + (unsigned long)alloc, + (unsigned long)env.obj, + (unsigned long)env.pad, + (unsigned long)free, + (unsigned long)total); + Insist(alloc == env.obj); + } + + for (i = 0; i < NELEMS(aps); ++i) { + mps_ap_destroy(aps[i].ap); + } + mps_pool_destroy(pool); + mps_fmt_destroy(fmt); + mps_arena_destroy(arena); +} + +int main(int argc, char *argv[]) +{ + testlib_init(argc, argv); + + test(mps_class_snc()); + + printf("%s: Conclusion: Failed to find any defects.\n", argv[0]); + return 0; +} + + +/* C. COPYRIGHT AND LICENSE + * + * Copyright (c) 2001-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: + * + * 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/mps/code/sp.h b/mps/code/sp.h index e2a8bef4507..e1c2e12de06 100644 --- a/mps/code/sp.h +++ b/mps/code/sp.h @@ -1,7 +1,7 @@ /* 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. + * $Id$ + * Copyright (c) 2014-2016 Ravenbrook Limited. See end of file for license. */ #ifndef sp_h @@ -29,7 +29,7 @@ extern void StackProbe(Size depth); /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2014 Ravenbrook Limited . + * Copyright (C) 2014-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/splay.c b/mps/code/splay.c index ed46c7a4ba4..8de21b46f16 100644 --- a/mps/code/splay.c +++ b/mps/code/splay.c @@ -1,7 +1,7 @@ /* splay.c: SPLAY TREE IMPLEMENTATION * * $Id$ - * Copyright (c) 2001-2015 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * * .purpose: Splay trees are used to manage potentially unbounded * collections of ordered things. In the MPS these are usually @@ -12,6 +12,12 @@ * .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. + * + * .critical: In manual-allocation-bound programs using MVFF, many of + * these functions are on the critical paths via mps_alloc (and then + * PoolAlloc, MVFFAlloc, failoverFind*, cbsFind*, SplayTreeFind*) and + * mps_free (and then MVFFFree, failoverInsert, cbsInsert, + * SplayTreeInsert). */ @@ -506,13 +512,15 @@ static Compare SplaySplitRev(SplayStateStruct *stateReturn, SplayTree splay, TreeKey key, TreeCompareFunction compare) { + SplayUpdateNodeFunction updateNode; Tree middle, leftLast, rightFirst; Compare cmp; - AVERT(SplayTree, splay); - AVER(FUNCHECK(compare)); - AVER(!SplayTreeIsEmpty(splay)); + AVERT_CRITICAL(SplayTree, splay); + AVER_CRITICAL(FUNCHECK(compare)); + AVER_CRITICAL(!SplayTreeIsEmpty(splay)); + updateNode = splay->updateNode; leftLast = TreeEMPTY; rightFirst = TreeEMPTY; middle = SplayTreeRoot(splay); @@ -540,7 +548,7 @@ static Compare SplaySplitRev(SplayStateStruct *stateReturn, if (!TreeHasLeft(middle)) goto stop; middle = SplayZigZigRev(middle, &rightFirst); - splay->updateNode(splay, TreeRight(rightFirst)); + updateNode(splay, TreeRight(rightFirst)); break; case CompareGREATER: if (!TreeHasRight(middle)) @@ -565,7 +573,7 @@ static Compare SplaySplitRev(SplayStateStruct *stateReturn, if (!TreeHasRight(middle)) goto stop; middle = SplayZagZagRev(middle, &leftLast); - splay->updateNode(splay, TreeLeft(leftLast)); + updateNode(splay, TreeLeft(leftLast)); break; case CompareLESS: if (!TreeHasLeft(middle)) @@ -589,13 +597,17 @@ static Compare SplaySplitRev(SplayStateStruct *stateReturn, static Tree SplayUpdateLeftSpine(SplayTree splay, Tree node, Tree child) { + SplayUpdateNodeFunction updateNode; + AVERT_CRITICAL(SplayTree, splay); AVERT_CRITICAL(Tree, node); AVERT_CRITICAL(Tree, child); + + updateNode = splay->updateNode; while(node != TreeEMPTY) { Tree parent = TreeLeft(node); TreeSetLeft(node, child); /* un-reverse pointer */ - splay->updateNode(splay, node); + updateNode(splay, node); child = node; node = parent; } @@ -606,13 +618,17 @@ static Tree SplayUpdateLeftSpine(SplayTree splay, Tree node, Tree child) static Tree SplayUpdateRightSpine(SplayTree splay, Tree node, Tree child) { + SplayUpdateNodeFunction updateNode; + AVERT_CRITICAL(SplayTree, splay); AVERT_CRITICAL(Tree, node); AVERT_CRITICAL(Tree, child); + + updateNode = splay->updateNode; while (node != TreeEMPTY) { Tree parent = TreeRight(node); TreeSetRight(node, child); /* un-reverse pointer */ - splay->updateNode(splay, node); + updateNode(splay, node); child = node; node = parent; } @@ -633,8 +649,8 @@ static void SplayAssembleRev(SplayTree splay, SplayState state) { Tree left, right; - AVERT(SplayTree, splay); - AVER(state->middle != TreeEMPTY); + AVERT_CRITICAL(SplayTree, splay); + AVER_CRITICAL(state->middle != TreeEMPTY); left = TreeLeft(state->middle); left = SplayUpdateRightSpine(splay, state->leftLast, left); @@ -725,7 +741,6 @@ static Compare SplaySplay(SplayTree splay, TreeKey key, /* SplayTreeInsert -- insert a node into a splay tree - * * * 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 @@ -737,7 +752,8 @@ static Compare SplaySplay(SplayTree splay, TreeKey key, * a good thing for key neighbours to be tree neighbours. */ -Bool SplayTreeInsert(SplayTree splay, Tree node) { +Bool SplayTreeInsert(SplayTree splay, Tree node) +{ Tree neighbour; AVERT(SplayTree, splay); @@ -753,7 +769,7 @@ Bool SplayTreeInsert(SplayTree splay, Tree node) { switch (SplaySplay(splay, splay->nodeKey(node), splay->compare)) { default: NOTREACHED; - /* defensive fall-through */ + /* fall through */ case CompareEQUAL: /* duplicate node */ return FALSE; @@ -793,7 +809,8 @@ Bool SplayTreeInsert(SplayTree splay, Tree node) { * avoid a search for a replacement in more cases. */ -Bool SplayTreeDelete(SplayTree splay, Tree node) { +Bool SplayTreeDelete(SplayTree splay, Tree node) +{ Tree leftLast; Compare cmp; @@ -840,7 +857,8 @@ Bool SplayTreeDelete(SplayTree splay, Tree node) { * node in the tree, otherwise ``*nodeReturn`` will be set to the node. */ -Bool SplayTreeFind(Tree *nodeReturn, SplayTree splay, TreeKey key) { +Bool SplayTreeFind(Tree *nodeReturn, SplayTree splay, TreeKey key) +{ AVERT(SplayTree, splay); AVER(nodeReturn != NULL); @@ -861,7 +879,8 @@ Bool SplayTreeFind(Tree *nodeReturn, SplayTree splay, TreeKey key) { * in which case TreeEMPTY is returned, and the tree is unchanged. */ -static Tree SplayTreeSuccessor(SplayTree splay) { +static Tree SplayTreeSuccessor(SplayTree splay) +{ Tree oldRoot, newRoot; AVERT(SplayTree, splay); @@ -915,10 +934,9 @@ Bool SplayTreeNeighbours(Tree *leftReturn, Tree *rightReturn, Count count = SplayDebugCount(splay); #endif - - AVERT(SplayTree, splay); - AVER(leftReturn != NULL); - AVER(rightReturn != NULL); + AVERT_CRITICAL(SplayTree, splay); + AVER_CRITICAL(leftReturn != NULL); + AVER_CRITICAL(rightReturn != NULL); if (SplayTreeIsEmpty(splay)) { *leftReturn = *rightReturn = TreeEMPTY; @@ -930,20 +948,20 @@ Bool SplayTreeNeighbours(Tree *leftReturn, Tree *rightReturn, switch (cmp) { default: NOTREACHED; - /* defensive fall-through */ + /* fall through */ case CompareEQUAL: found = FALSE; break; case CompareLESS: - AVER(!TreeHasLeft(stateStruct.middle)); + AVER_CRITICAL(!TreeHasLeft(stateStruct.middle)); *rightReturn = stateStruct.middle; *leftReturn = stateStruct.leftLast; found = TRUE; break; case CompareGREATER: - AVER(!TreeHasRight(stateStruct.middle)); + AVER_CRITICAL(!TreeHasRight(stateStruct.middle)); *leftReturn = stateStruct.middle; *rightReturn = stateStruct.rightFirst; found = TRUE; @@ -978,7 +996,8 @@ Bool SplayTreeNeighbours(Tree *leftReturn, Tree *rightReturn, * shape caused by previous splays. Consider using TreeTraverse instead. */ -Tree SplayTreeFirst(SplayTree splay) { +Tree SplayTreeFirst(SplayTree splay) +{ Tree node; AVERT(SplayTree, splay); @@ -994,7 +1013,8 @@ Tree SplayTreeFirst(SplayTree splay) { return node; } -Tree SplayTreeNext(SplayTree splay, TreeKey oldKey) { +Tree SplayTreeNext(SplayTree splay, TreeKey oldKey) +{ AVERT(SplayTree, splay); if (SplayTreeIsEmpty(splay)) @@ -1005,7 +1025,7 @@ Tree SplayTreeNext(SplayTree splay, TreeKey oldKey) { switch (SplaySplay(splay, oldKey, splay->compare)) { default: NOTREACHED; - /* defensive fall-through */ + /* fall through */ case CompareLESS: return SplayTreeRoot(splay); @@ -1088,38 +1108,35 @@ static Res SplayNodeDescribe(Tree node, mps_lib_FILE *stream, typedef struct SplayFindClosureStruct { SplayTestNodeFunction testNode; SplayTestTreeFunction testTree; - void *p; - Size s; + void *testClosure; SplayTree splay; Bool found; } SplayFindClosureStruct, *SplayFindClosure; static Compare SplayFindFirstCompare(Tree node, TreeKey key) { - SplayFindClosure closure; - void *closureP; - Size closureS; + SplayFindClosure my; SplayTestNodeFunction testNode; SplayTestTreeFunction testTree; + void *testClosure; SplayTree splay; - AVERT(Tree, node); - AVER(key != NULL); + AVERT_CRITICAL(Tree, node); + AVER_CRITICAL(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; - splay = closure->splay; + my = (SplayFindClosure)key; + testClosure = my->testClosure; + testNode = my->testNode; + testTree = my->testTree; + splay = my->splay; if (TreeHasLeft(node) && - (*testTree)(splay, TreeLeft(node), closureP, closureS)) { + (*testTree)(splay, TreeLeft(node), testClosure)) { return CompareLESS; - } else if ((*testNode)(splay, node, closureP, closureS)) { - closure->found = TRUE; + } else if ((*testNode)(splay, node, testClosure)) { + my->found = TRUE; return CompareEQUAL; } else { /* If there's a right subtree but it doesn't satisfy the tree test @@ -1127,8 +1144,8 @@ static Compare SplayFindFirstCompare(Tree node, TreeKey key) 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; + !(*testTree)(splay, TreeRight(node), testClosure)) { + my->found = FALSE; return CompareEQUAL; } return CompareGREATER; @@ -1137,36 +1154,34 @@ static Compare SplayFindFirstCompare(Tree node, TreeKey key) static Compare SplayFindLastCompare(Tree node, TreeKey key) { - SplayFindClosure closure; - void *closureP; - Size closureS; + SplayFindClosure my; SplayTestNodeFunction testNode; SplayTestTreeFunction testTree; + void *testClosure; SplayTree splay; - AVERT(Tree, node); - AVER(key != NULL); + AVERT_CRITICAL(Tree, node); + AVER_CRITICAL(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; - splay = closure->splay; + my = (SplayFindClosure)key; + testClosure = my->testClosure; + testNode = my->testNode; + testTree = my->testTree; + splay = my->splay; if (TreeHasRight(node) && - (*testTree)(splay, TreeRight(node), closureP, closureS)) { - return CompareGREATER; - } else if ((*testNode)(splay, node, closureP, closureS)) { - closure->found = TRUE; + (*testTree)(splay, TreeRight(node), testClosure)) { + return CompareGREATER; + } else if ((*testNode)(splay, node, testClosure)) { + my->found = TRUE; return CompareEQUAL; } else { /* See SplayFindFirstCompare. */ if (TreeHasLeft(node) && - !(*testTree)(splay, TreeLeft(node), closureP, closureS)) { - closure->found = FALSE; + !(*testTree)(splay, TreeLeft(node), testClosure)) { + my->found = FALSE; return CompareEQUAL; } return CompareLESS; @@ -1184,8 +1199,8 @@ static Compare SplayFindLastCompare(Tree node, TreeKey key) * ``*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. + * a single node or a sub-tree rooted at a node, and both receive an + * arbitrary closure. * * TODO: This repeatedly splays failed matches to the root and rotates * them, so it could have quite an unbalancing effect if size is small. @@ -1195,22 +1210,21 @@ static Compare SplayFindLastCompare(Tree node, TreeKey key) Bool SplayFindFirst(Tree *nodeReturn, SplayTree splay, SplayTestNodeFunction testNode, SplayTestTreeFunction testTree, - void *closureP, Size closureS) + void *testClosure) { SplayFindClosureStruct closureStruct; Bool found; - AVER(nodeReturn != NULL); - AVERT(SplayTree, splay); - AVER(FUNCHECK(testNode)); - AVER(FUNCHECK(testTree)); + AVER_CRITICAL(nodeReturn != NULL); + AVERT_CRITICAL(SplayTree, splay); + AVER_CRITICAL(FUNCHECK(testNode)); + AVER_CRITICAL(FUNCHECK(testTree)); if (SplayTreeIsEmpty(splay) || - !testTree(splay, SplayTreeRoot(splay), closureP, closureS)) + !testTree(splay, SplayTreeRoot(splay), testClosure)) return FALSE; /* no suitable nodes in tree */ - closureStruct.p = closureP; - closureStruct.s = closureS; + closureStruct.testClosure = testClosure; closureStruct.testNode = testNode; closureStruct.testTree = testTree; closureStruct.splay = splay; @@ -1227,7 +1241,7 @@ Bool SplayFindFirst(Tree *nodeReturn, SplayTree splay, oldRoot = SplayTreeRoot(splay); newRoot = TreeRight(oldRoot); - if (newRoot == TreeEMPTY || !(*testTree)(splay, newRoot, closureP, closureS)) + if (newRoot == TreeEMPTY || !(*testTree)(splay, newRoot, testClosure)) return FALSE; /* no suitable nodes in the rest of the tree */ /* Temporarily chop off the left half-tree, inclusive of root, @@ -1259,22 +1273,21 @@ Bool SplayFindFirst(Tree *nodeReturn, SplayTree splay, Bool SplayFindLast(Tree *nodeReturn, SplayTree splay, SplayTestNodeFunction testNode, SplayTestTreeFunction testTree, - void *closureP, Size closureS) + void *testClosure) { SplayFindClosureStruct closureStruct; Bool found; - AVER(nodeReturn != NULL); - AVERT(SplayTree, splay); - AVER(FUNCHECK(testNode)); - AVER(FUNCHECK(testTree)); + AVER_CRITICAL(nodeReturn != NULL); + AVERT_CRITICAL(SplayTree, splay); + AVER_CRITICAL(FUNCHECK(testNode)); + AVER_CRITICAL(FUNCHECK(testTree)); if (SplayTreeIsEmpty(splay) || - !testTree(splay, SplayTreeRoot(splay), closureP, closureS)) + !testTree(splay, SplayTreeRoot(splay), testClosure)) return FALSE; /* no suitable nodes in tree */ - closureStruct.p = closureP; - closureStruct.s = closureS; + closureStruct.testClosure = testClosure; closureStruct.testNode = testNode; closureStruct.testTree = testTree; closureStruct.splay = splay; @@ -1289,7 +1302,7 @@ Bool SplayFindLast(Tree *nodeReturn, SplayTree splay, oldRoot = SplayTreeRoot(splay); newRoot = TreeLeft(oldRoot); - if (newRoot == TreeEMPTY || !(*testTree)(splay, newRoot, closureP, closureS)) + if (newRoot == TreeEMPTY || !(*testTree)(splay, newRoot, testClosure)) return FALSE; /* no suitable nodes in the rest of the tree */ /* Temporarily chop off the right half-tree, inclusive of root, @@ -1335,7 +1348,6 @@ void SplayNodeRefresh(SplayTree splay, Tree node) 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); @@ -1353,7 +1365,6 @@ void SplayNodeInit(SplayTree splay, Tree node) 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); } @@ -1401,7 +1412,7 @@ Res SplayTreeDescribe(SplayTree splay, mps_lib_FILE *stream, Count depth, /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2015 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/splay.h b/mps/code/splay.h index c1b109e64a7..d9cf821eb90 100644 --- a/mps/code/splay.h +++ b/mps/code/splay.h @@ -16,9 +16,9 @@ typedef struct SplayTreeStruct *SplayTree; typedef Bool (*SplayTestNodeFunction)(SplayTree splay, Tree node, - void *closureP, Size closureS); + void *closure); typedef Bool (*SplayTestTreeFunction)(SplayTree splay, Tree node, - void *closureP, Size closureS); + void *closure); typedef void (*SplayUpdateNodeFunction)(SplayTree splay, Tree node); extern void SplayTrivUpdate(SplayTree splay, Tree node); @@ -58,15 +58,15 @@ extern Tree SplayTreeNext(SplayTree splay, TreeKey oldKey); typedef Bool (*SplayFindFunction)(Tree *nodeReturn, SplayTree splay, SplayTestNodeFunction testNode, SplayTestTreeFunction testTree, - void *closureP, Size closureS); + void *closure); extern Bool SplayFindFirst(Tree *nodeReturn, SplayTree splay, SplayTestNodeFunction testNode, SplayTestTreeFunction testTree, - void *closureP, Size closureS); + void *closure); extern Bool SplayFindLast(Tree *nodeReturn, SplayTree splay, SplayTestNodeFunction testNode, SplayTestTreeFunction testTree, - void *closureP, Size closureS); + void *closure); extern void SplayNodeRefresh(SplayTree splay, Tree node); extern void SplayNodeInit(SplayTree splay, Tree node); diff --git a/mps/code/spw3i3.c b/mps/code/spw3i3.c index 1439d069c40..c09d46cbbfb 100644 --- a/mps/code/spw3i3.c +++ b/mps/code/spw3i3.c @@ -1,7 +1,7 @@ /* spw3i3.c: STACK PROBE FOR 32-BIT WINDOWS * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * Portions copyright (C) 2001 Global Graphics Software. * * This function reads a location that is depth words beyond the @@ -13,6 +13,10 @@ #include "mpm.h" +#if !defined(MPS_OS_W3) || !defined(MPS_ARCH_I3) +#error "spw3i3.c is specific to MPS_OS_W3 and MPS_ARCH_I3" +#endif + #ifdef MPS_BUILD_PC /* "[ISO] Inline assembly code is not portable." */ @@ -33,7 +37,7 @@ void StackProbe(Size depth) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/spw3i6.c b/mps/code/spw3i6.c index 90997cd1589..c00dda8e044 100644 --- a/mps/code/spw3i6.c +++ b/mps/code/spw3i6.c @@ -1,7 +1,7 @@ /* spw3i6.c: STACK PROBE FOR 64-BIT WINDOWS * * $Id$ - * Copyright (c) 2013-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2013-2018 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 @@ -10,19 +10,24 @@ * _alloca: http://msdn.microsoft.com/en-us/library/wb1s57t5.aspx */ +#include "mpm.h" + +#if !defined(MPS_OS_W3) +#error "spw3i6.c is specific to MPS_OS_W3" +#endif + #include /* _alloca */ -#include "mpm.h" void StackProbe(Size depth) { - (void)_alloca(depth*sizeof(Word)); + (void)_alloca(depth * sizeof(Word)); } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2013-2014 Ravenbrook Limited . + * Copyright (C) 2013-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/ss.c b/mps/code/ss.c index 506001d04d2..bc6e469f037 100644 --- a/mps/code/ss.c +++ b/mps/code/ss.c @@ -3,13 +3,20 @@ * $Id$ * 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 + * This scans the mutator's stack and fixes the registers that may + * contain roots. See . * - * Each platform ABI has a set of callee-save registers that may still - * contain roots. The StackScan function is defined for each ABI in source - * files like ss*.c and ss*.asm. That function saves the callee save - * registers in its frame, then calls StackScanInner to do the scanning. + * This is a generic implementation, but it makes assumptions that, + * while true on all the platforms we currently (version 1.115) + * support, may not be true on all platforms. See + * . + * + * .assume.desc: The stack is descending (and so stackHot is a lower + * address than stackCold). + * + * .assume.full: The stack convention is "full" (and so we must scan + * the word pointed to by stackHot but not the word pointed to by + * stackCold). */ #include "mpm.h" @@ -17,58 +24,46 @@ SRCID(ss, "$Id$"); -/* StackScanInner -- carry out stack scanning +/* StackHot -- capture a hot stack pointer * - * This function should be called by StackScan once it has saved the - * callee-save registers for the platform ABI in order to do the actual - * scanning. + * On all supported platforms, the arguments are pushed on to the + * stack by the caller below its other local data, so as long as + * it does not use something like alloca, the address of the argument + * is a hot stack pointer. See design.mps.ss.sol.stack.hot. */ -Res StackScanInner(ScanState ss, Word *stackCold, Word *stackHot, - Count nSavedRegs, - mps_area_scan_t scan_area, void *closure) +ATTRIBUTE_NOINLINE +void StackHot(void **stackOut) { + *stackOut = &stackOut; +} + + +/* StackScan -- scan the mutator's stack and registers */ + +Res StackScan(ScanState ss, void *stackCold, + mps_area_scan_t scan_area, void *closure) +{ + StackContextStruct scStruct; Arena arena; - Res res; + void *warmest; AVERT(ScanState, ss); - AVER(0 < nSavedRegs); - AVER(nSavedRegs < 128); /* sanity check */ - - /* .assume.stack: This implementation assumes that the stack grows - * downwards, so that the address of the jmp_buf is the base of the - * part of the stack that needs to be scanned. (StackScanInner makes - * the same assumption.) - */ - AVER(stackHot < stackCold); arena = ss->arena; - /* If a stack pointer was stored when we entered the arena (through the - MPS interface in mpsi*.c) then we scan just the saved registers and - the stack starting there, in order to avoid false ambiguous references - in the MPS stack. This is particularly important for transforms - (trans.c). Otherwise, scan the whole stack. */ - - if (arena->stackAtArenaEnter != NULL) { - AVER(stackHot < arena->stackAtArenaEnter); /* .assume.stack */ - AVER(arena->stackAtArenaEnter < stackCold); /* .assume.stack */ - res = TraceScanArea(ss, stackHot, stackHot + nSavedRegs, - scan_area, closure); - if (res != ResOK) - return res; - res = TraceScanArea(ss, arena->stackAtArenaEnter, stackCold, - scan_area, closure); - if (res != ResOK) - return res; - } else { - res = TraceScanArea(ss, stackHot, stackCold, - scan_area, closure); - if (res != ResOK) - return res; + AVER(arena->stackWarm != NULL); + warmest = arena->stackWarm; + if (warmest == NULL) { + /* Somehow missed saving the context at the entry point (see + ): do it now. */ + STACK_CONTEXT_SAVE(&scStruct); + warmest = &scStruct; } - return ResOK; + AVER(warmest < stackCold); /* .assume.desc */ + + return TraceScanArea(ss, warmest, stackCold, scan_area, closure); } diff --git a/mps/code/ss.h b/mps/code/ss.h index 75d939194e2..b784746dc3f 100644 --- a/mps/code/ss.h +++ b/mps/code/ss.h @@ -3,7 +3,9 @@ * $Id$ * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. * - * Provides a function for scanning the stack and registers + * This module saves the mutator context on entry to the MPS, and + * provides functions for decoding the context and scanning the root + * registers. See . */ #ifndef ss_h @@ -11,32 +13,79 @@ #include "mpm.h" - -/* StackScan -- scan the current thread's stack + +/* StackContext -- some of the mutator's state * - * StackScan scans the stack of the current thread, Between stackCold - * and the current hot end of the stack. It also fixes any roots which - * may be in callee-save registers. - * - * See the specific implementation for the exact registers which are scanned. - * - * If a stack pointer has been stashed at arena entry (through the MPS - * interface in mpsi*.c) then only the registers and the stack between - * stackAtArenaEnter and stackCold is scanned, to avoid scanning false - * ambiguous references on the MPS's own stack. This is particularly - * important for transforms (trans.c). - * - * The word pointed to by stackCold is fixed if the stack is by convention - * empty, and not fixed if it is full. Where empty means sp points to first - * free word beyond the top of stack. Full means sp points to the top of - * stack itself. + * The jumpBuffer is used to capture most of the mutator's state on + * entry to the MPS, but can't capture it all. See + * design.mps.stack-scan.sol.setjmp.scan. */ -extern Res StackScan(ScanState ss, Word *stackCold, - mps_area_scan_t scan_area, void *closure); -extern Res StackScanInner(ScanState ss, Word *stackCold, Word *stackHot, - Count nSavedRegs, - mps_area_scan_t scan_area, void *closure); +#include + +typedef struct StackContextStruct { + jmp_buf jumpBuffer; +} StackContextStruct; + + +/* StackHot -- capture a hot stack pointer + * + * Sets *stackOut to a stack pointer that includes the current frame. + */ + +void StackHot(void **stackOut); + + +/* STACK_CONTEXT_BEGIN -- save context */ + +#define STACK_CONTEXT_BEGIN(arena) \ + BEGIN \ + StackContextStruct _sc; \ + STACK_CONTEXT_SAVE(&_sc); \ + AVER(arena->stackWarm == NULL); \ + StackHot(&arena->stackWarm); \ + AVER(arena->stackWarm < (void *)&_sc); /* */ \ + BEGIN + + +/* STACK_CONTEXT_END -- clear context */ + +#define STACK_CONTEXT_END(arena) \ + END; \ + AVER(arena->stackWarm != NULL); \ + arena->stackWarm = NULL; \ + END + + +/* STACK_CONTEXT_SAVE -- save the callee-saves and stack pointer */ + +#if defined(MPS_OS_XC) + +/* We call _setjmp rather than setjmp because we can be confident what + * it does via the source code at + * , + * and because _setjmp saves only the register set and the stack while + * setjmp also saves the signal mask, which we don't care about. See + * _setjmp(2). */ + +#define STACK_CONTEXT_SAVE(sc) ((void)_setjmp((sc)->jumpBuffer)) + +#else /* other platforms */ + +#define STACK_CONTEXT_SAVE(sc) ((void)setjmp((sc)->jumpBuffer)) + +#endif /* platform defines */ + + +/* StackScan -- scan the mutator's stack and registers + * + * This must be called between STACK_CONTEXT_BEGIN and + * STACK_CONTEXT_END. + */ + +extern Res StackScan(ScanState ss, void *stackCold, + mps_area_scan_t scan_area, void *closure); + #endif /* ss_h */ diff --git a/mps/code/ssixi3.c b/mps/code/ssixi3.c deleted file mode 100644 index a922cabc5a6..00000000000 --- a/mps/code/ssixi3.c +++ /dev/null @@ -1,111 +0,0 @@ -/* ssixi3.c: UNIX/INTEL STACK SCANNING - * - * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. - * - * This scans the stack and fixes the registers which may contain - * roots. See - * - * This code was originally developed and tested on Linux, and then - * copied to the FreeBSD and Darwin (OS X) operating systems where it - * also seems to work. Note that on FreeBSD and Darwin it has not - * been indepently verified with respect to any ABI documentation. - * - * This code is common to more than one Unix implementation on - * Intel hardware (but is not portable Unix code). - * - * The registers edi, esi, ebx are the registers defined to be preserved - * across function calls and therefore may contain roots. - * These are pushed on the stack for scanning. - * - * SOURCES - * - * .source.callees.saves: Set of callee-saved registers taken from - * CALL_USED_REGISTERS in /config/i386/i386.h. - * ebp added to the list because gcc now doesn't always use it as - * a frame pointer so it could contain a root. - * - * ASSUMPTIONS - * - * .assume.align: The stack pointer is assumed to be aligned on a word - * boundary. - * - * .assume.asm.stack: The compiler must not do wacky things with the - * stack pointer around a call since we need to ensure that the - * callee-save regs are visible during TraceScanArea. - * - * .assume.asm.order: The volatile modifier should prevent movement - * of code, which might break .assume.asm.stack. - * - */ - - -#include "mpm.h" - -SRCID(ssixi3, "$Id$"); - - -/* .assume.asm.order */ -#define ASMV(x) __asm__ volatile (x) - - -Res StackScan(ScanState ss, Word *stackCold, - mps_area_scan_t scan_area, - void *closure) -{ - Word calleeSaveRegs[4]; - - /* .assume.asm.stack */ - /* Store the callee save registers on the stack so they get scanned - * as they may contain roots. - */ - ASMV("mov %%ebx, %0" : "=m" (calleeSaveRegs[0])); - ASMV("mov %%esi, %0" : "=m" (calleeSaveRegs[1])); - ASMV("mov %%edi, %0" : "=m" (calleeSaveRegs[2])); - ASMV("mov %%ebp, %0" : "=m" (calleeSaveRegs[3])); - - return StackScanInner(ss, stackCold, calleeSaveRegs, NELEMS(calleeSaveRegs), - scan_area, closure); -} - - -/* C. COPYRIGHT AND LICENSE - * - * Copyright (C) 2001-2002 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/mps/code/ssixi6.c b/mps/code/ssixi6.c deleted file mode 100644 index 2d70d62697c..00000000000 --- a/mps/code/ssixi6.c +++ /dev/null @@ -1,111 +0,0 @@ -/* ssixi6.c: UNIX/x64 STACK SCANNING - * - * $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 - * - * This code was branched from ssixi3.c (32-bit Intel) initially for the - * 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, - * all the non-Windows platforms use the System V AMD64 ABI. See - * .sources.callees.saves. - * - * SOURCES - * - * .sources.callees.saves: - * "Registers %rbp, %rbx and %r12 through %r15 "belong" to the calling - * function and the called function is required to preserve their values. - * In other words, a called function must preserve these registers’ values - * for its caller." -- System V AMD64 ABI - * - * - * ASSUMPTIONS - * - * .assume.align: The stack pointer is assumed to be aligned on a word - * boundary. - * - * .assume.asm.stack: The compiler must not do wacky things with the - * stack pointer around a call since we need to ensure that the - * callee-save regs are visible during TraceScanArea. - * - * .assume.asm.order: The volatile modifier should prevent movement - * of code, which might break .assume.asm.stack. - * - */ - - -#include "mpm.h" - -SRCID(ssixi6, "$Id$"); - - -/* .assume.asm.order */ -#define ASMV(x) __asm__ volatile (x) - - -Res StackScan(ScanState ss, Word *stackCold, - mps_area_scan_t scan_area, - void *closure) -{ - Word calleeSaveRegs[6]; - - /* .assume.asm.stack */ - /* Store the callee save registers on the stack so they get scanned - * as they may contain roots. - */ - ASMV("mov %%rbp, %0" : "=m" (calleeSaveRegs[0])); - ASMV("mov %%rbx, %0" : "=m" (calleeSaveRegs[1])); - ASMV("mov %%r12, %0" : "=m" (calleeSaveRegs[2])); - ASMV("mov %%r13, %0" : "=m" (calleeSaveRegs[3])); - ASMV("mov %%r14, %0" : "=m" (calleeSaveRegs[4])); - ASMV("mov %%r15, %0" : "=m" (calleeSaveRegs[5])); - - return StackScanInner(ss, stackCold, calleeSaveRegs, NELEMS(calleeSaveRegs), - scan_area, closure); -} - - -/* 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/mps/code/ssw3i3mv.c b/mps/code/ssw3i3mv.c deleted file mode 100644 index 2b2d0bd4d08..00000000000 --- a/mps/code/ssw3i3mv.c +++ /dev/null @@ -1,90 +0,0 @@ -/* ssw3i3mv.c: STACK SCANNING FOR WIN32 WITH MICROSOFT C - * - * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. - * - * This scans the stack and fixes the registers which may contain roots. - * See . - * - * 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(ssw3i3mv, "$Id$"); - - -Res StackScan(ScanState ss, Word *stackCold, - mps_area_scan_t scan_area, - void *closure) -{ - jmp_buf jb; - - /* We rely on the fact that Microsoft 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)->Ebx) == sizeof(Word)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->Edi) == sizeof(Word)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->Esi) == sizeof(Word)); - - /* 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, stackCold, (Word *)&((_JUMP_BUFFER *)jb)->Ebx, 3, - scan_area, closure); -} - -/* C. COPYRIGHT AND LICENSE - * - * Copyright (C) 2001-2002 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/mps/code/ssw3i3pc.c b/mps/code/ssw3i3pc.c deleted file mode 100644 index bead20fe898..00000000000 --- a/mps/code/ssw3i3pc.c +++ /dev/null @@ -1,114 +0,0 @@ -/* 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, Word *stackCold, - mps_area_scan_t scan_area, - void *closure) -{ - jmp_buf jb; - - /* .assume.ms-compat */ - (void)setjmp(jb); - - /* These checks, on the _JUMP_BUFFER defined above, are mainly here - * to maintain similarity to the matching code on the MPS_BUILD_MV - * version of this code. */ - AVER(sizeof(((_JUMP_BUFFER *)jb)->Ebx) == sizeof(Word)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->Edi) == sizeof(Word)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->Esi) == sizeof(Word)); - - /* 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, stackCold, (Word *)&((_JUMP_BUFFER *)jb)->Ebx, 3, - scan_area, closure); -} - - -/* 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/mps/code/ssw3i6mv.c b/mps/code/ssw3i6mv.c deleted file mode 100644 index c95f8c30608..00000000000 --- a/mps/code/ssw3i6mv.c +++ /dev/null @@ -1,108 +0,0 @@ -/* ssw3i6mv.c: STACK SCANNING FOR WIN64 WITH MICROSOFT 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 . It's unlikely that the callee-save - * registers contain mutator roots by the time this function is called, but - * we can't be certain, so we must scan them anyway. - * - * 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(ssw3i6mv, "$Id$"); - - -Res StackScan(ScanState ss, Word *stackCold, - mps_area_scan_t scan_area, void *closure) -{ - jmp_buf jb; - - /* We rely on the fact that Microsoft 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)->Rbx) == sizeof(Word)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->Rsp) == sizeof(Word)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->Rbp) == sizeof(Word)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->Rsi) == sizeof(Word)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->Rdi) == sizeof(Word)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->R12) == sizeof(Word)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->R13) == sizeof(Word)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->R14) == sizeof(Word)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->R15) == sizeof(Word)); - - /* 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, stackCold, (Word *)&((_JUMP_BUFFER *)jb)->Rbx, 9, - scan_area, closure); -} - -/* 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/mps/code/ssw3i6pc.c b/mps/code/ssw3i6pc.c deleted file mode 100644 index 5e8818282ac..00000000000 --- a/mps/code/ssw3i6pc.c +++ /dev/null @@ -1,146 +0,0 @@ -/* 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, Word *stackCold, Word mask, Word pattern) -{ - 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, on the _JUMP_BUFFER defined above, are mainly here - * to maintain similarity to the matching code on the MPS_BUILD_MV - * version of this code. */ - AVER(sizeof(((_JUMP_BUFFER *)jb)->Rbx) == sizeof(Word)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->Rsp) == sizeof(Word)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->Rbp) == sizeof(Word)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->Rsi) == sizeof(Word)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->Rdi) == sizeof(Word)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->R12) == sizeof(Word)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->R13) == sizeof(Word)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->R14) == sizeof(Word)); - AVER(sizeof(((_JUMP_BUFFER *)jb)->R15) == sizeof(Word)); - - /* 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, stackCold, (Word *)&((_JUMP_BUFFER *)jb)->Rbx, 9, - mask, pattern); -} - - -/* 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/mps/code/table.c b/mps/code/table.c index b8d0576e759..a26ed499d13 100644 --- a/mps/code/table.c +++ b/mps/code/table.c @@ -1,7 +1,7 @@ /* table.h: A dictionary mapping a Word to a void* * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 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. @@ -220,13 +220,9 @@ Res TableGrow(Table table, Count extraCapacity) /* TableCreate -- makes a new table */ -extern Res TableCreate(Table *tableReturn, - Count length, - TableAllocFunction tableAlloc, - TableFreeFunction tableFree, - void *allocClosure, - TableKey unusedKey, - TableKey deletedKey) +Res TableCreate(Table *tableReturn, Count length, + TableAllocFunction tableAlloc, TableFreeFunction tableFree, + void *allocClosure, TableKey unusedKey, TableKey deletedKey) { Table table; Res res; @@ -263,7 +259,7 @@ extern Res TableCreate(Table *tableReturn, /* TableDestroy -- destroy a table */ -extern void TableDestroy(Table table) +void TableDestroy(Table table) { AVER(table != NULL); if (table->length > 0) { @@ -279,7 +275,7 @@ extern void TableDestroy(Table table) /* TableLookup -- look up */ -extern Bool TableLookup(TableValue *valueReturn, Table table, TableKey key) +Bool TableLookup(TableValue *valueReturn, Table table, TableKey key) { TableEntry entry = tableFind(table, key, TRUE /* skip deleted */); @@ -292,7 +288,7 @@ extern Bool TableLookup(TableValue *valueReturn, Table table, TableKey key) /* TableDefine -- add a new mapping */ -extern Res TableDefine(Table table, TableKey key, TableValue value) +Res TableDefine(Table table, TableKey key, TableValue value) { TableEntry entry; @@ -326,7 +322,7 @@ extern Res TableDefine(Table table, TableKey key, TableValue value) /* TableRedefine -- redefine an existing mapping */ -extern Res TableRedefine(Table table, TableKey key, TableValue value) +Res TableRedefine(Table table, TableKey key, TableValue value) { TableEntry entry; @@ -344,7 +340,7 @@ extern Res TableRedefine(Table table, TableKey key, TableValue value) /* TableRemove -- remove a mapping */ -extern Res TableRemove(Table table, TableKey key) +Res TableRemove(Table table, TableKey key) { TableEntry entry; @@ -362,9 +358,9 @@ extern Res TableRemove(Table table, TableKey key) /* TableMap -- apply a function to all the mappings */ -extern void TableMap(Table table, - void (*fun)(void *closure, TableKey key, TableValue value), - void *closure) +void TableMap(Table table, + void (*fun)(void *closure, TableKey key, TableValue value), + void *closure) { Index i; for (i = 0; i < table->length; i++) @@ -375,7 +371,7 @@ extern void TableMap(Table table, /* TableCount -- count the number of mappings in the table */ -extern Count TableCount(Table table) +Count TableCount(Table table) { return table->count; } @@ -383,7 +379,7 @@ extern Count TableCount(Table table) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/tagtest.c b/mps/code/tagtest.c index fda27efd9b7..9b3dea53c60 100644 --- a/mps/code/tagtest.c +++ b/mps/code/tagtest.c @@ -1,7 +1,7 @@ /* tagtest.c: TAGGED POINTER TEST * * $Id$ - * Copyright (c) 2015 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2015-2016 Ravenbrook Limited. See end of file for license. * * .overview: This test case checks that the MPS correctly handles * tagged pointers via the object format and tagged area scanning. @@ -77,9 +77,12 @@ static void pad(mps_addr_t addr, size_t size) } } +static mps_arena_t arena; + static mps_res_t scan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) { + Insist(mps_arena_busy(arena)); MPS_SCAN_BEGIN(ss) { mps_word_t *p = base; while (p < (mps_word_t *)limit) { @@ -106,7 +109,7 @@ static mps_addr_t skip(mps_addr_t addr) } -static void collect(mps_arena_t arena, size_t expected) +static void collect(size_t expected) { size_t finalized = 0; mps_arena_collect(arena); @@ -148,7 +151,6 @@ static const char *mode_name[] = { static void test(int mode) { - mps_arena_t arena; mps_thr_t thread; mps_root_t root; mps_fmt_t fmt; @@ -214,7 +216,7 @@ static void test(int mode) die(mps_finalize(arena, &addr), "finalize"); } - collect(arena, expected); + collect(expected); mps_arena_park(arena); mps_ap_destroy(ap); @@ -267,7 +269,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2015 Ravenbrook Limited . + * Copyright (c) 2015-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/teletest.c b/mps/code/teletest.c index 1a6b1614553..8d811038b94 100644 --- a/mps/code/teletest.c +++ b/mps/code/teletest.c @@ -1,7 +1,7 @@ /* teletest.c: TELEMETRY TEST * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * * .source: The command parser here was taken and adapted from bttest.c. */ @@ -191,7 +191,7 @@ static void obeyCommand(char *command) #define testArenaSIZE (((size_t)64)<<20) -extern int main(int argc, char *argv[]) +int main(int argc, char *argv[]) { testlib_init(argc, argv); @@ -216,7 +216,7 @@ extern int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/testlib.c b/mps/code/testlib.c index 401c4059938..cd8f340ba36 100644 --- a/mps/code/testlib.c +++ b/mps/code/testlib.c @@ -1,7 +1,7 @@ /* testlib.c: TEST LIBRARY * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 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. @@ -12,7 +12,7 @@ #include "mps.h" #include "misc.h" /* for NOOP */ -#include /* fmod, log */ +#include /* fmod, log, HUGE_VAL */ #include /* fflush, printf, stderr, sscanf, vfprintf */ #include /* abort, exit, getenv */ #include /* time */ @@ -220,14 +220,39 @@ double rnd_double(void) return rnd() / R_m_float; } +static unsigned sizelog2(size_t size) +{ + return (unsigned)(log((double)size) / log(2.0)); +} + 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)); + of zones in the initial chunk, but bigger than one word. */ + Insist(arena_size >> MPS_WORD_SHIFT >= sizeof(void *)); + return rnd_align(sizeof(void *), (size_t)1 << sizelog2(arena_size >> MPS_WORD_SHIFT)); +} + +size_t rnd_align(size_t min, size_t max) +{ + unsigned log2min = sizelog2(min); + unsigned log2max = sizelog2(max); + Insist(min <= max); + Insist((size_t)1 << log2min == min); + Insist((size_t)1 << log2max == max); + if (log2min < log2max) + return min << (rnd() % (log2max - log2min + 1)); + else + return min; +} + +double rnd_pause_time(void) +{ + double t = rnd_double(); + if (t == 0.0) + return HUGE_VAL; /* Would prefer to use INFINITY but it's not in C89. */ + else + return 1 / t - 1; } rnd_state_t rnd_seed(void) @@ -363,7 +388,7 @@ void error(const char *format, ...) va_start(args, format); verror(format, args); - va_end(args); + /* va_end(args); */ /* provokes "unreachable code" error from MSVC */ } @@ -417,7 +442,7 @@ void testlib_init(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (c) 2001-2014 Ravenbrook Limited . + * Copyright (c) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/testlib.h b/mps/code/testlib.h index 0b5b7160165..00ef7ba6fe6 100644 --- a/mps/code/testlib.h +++ b/mps/code/testlib.h @@ -47,13 +47,9 @@ */ #define ATTRIBUTE_FORMAT(ARGLIST) __attribute__((__format__ ARGLIST)) -/* GCC: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html */ -#define ATTRIBUTE_NOINLINE __attribute__((__noinline__)) - #else #define ATTRIBUTE_FORMAT(ARGLIST) -#define ATTRIBUTE_NOINLINE #endif @@ -260,6 +256,16 @@ extern double rnd_double(void); extern size_t rnd_grain(size_t arena_size); +/* rnd_align -- random alignment */ + +extern size_t rnd_align(size_t min, size_t max); + + +/* rnd_pause_time -- random pause time */ + +extern double rnd_pause_time(void); + + /* randomize -- randomize the generator, or initialize to replay * * randomize(argc, argv) randomizes the rnd generator (using time(3)) diff --git a/mps/code/testthr.h b/mps/code/testthr.h index 75602bcc56f..3edceda068c 100644 --- a/mps/code/testthr.h +++ b/mps/code/testthr.h @@ -1,7 +1,7 @@ /* 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. + * $Id$ + * Copyright (c) 2014-2016 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 @@ -83,7 +83,7 @@ void testthr_join(testthr_t *thread, void **result_o); /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2014 Ravenbrook Limited . + * Copyright (C) 2014-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/testthrix.c b/mps/code/testthrix.c index f4c544e0b8a..90849158b2d 100644 --- a/mps/code/testthrix.c +++ b/mps/code/testthrix.c @@ -1,7 +1,7 @@ /* 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. + * $Id$ + * Copyright (c) 2014-2016 Ravenbrook Limited. See end of file for license. */ #include "testlib.h" @@ -26,7 +26,7 @@ void testthr_join(testthr_t *thread, void **result_o) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2014 Ravenbrook Limited . + * Copyright (C) 2014-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/testthrw3.c b/mps/code/testthrw3.c index cd5cf54ae79..db7fc88709a 100644 --- a/mps/code/testthrw3.c +++ b/mps/code/testthrw3.c @@ -1,7 +1,7 @@ /* 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. + * $Id$ + * Copyright (c) 2014-2016 Ravenbrook Limited. See end of file for license. */ #include "testlib.h" @@ -40,7 +40,7 @@ void testthr_join(testthr_t *thread, void **result_o) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2014 Ravenbrook Limited . + * Copyright (C) 2014-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/th.h b/mps/code/th.h index 29a4d243d1d..63140a18672 100644 --- a/mps/code/th.h +++ b/mps/code/th.h @@ -68,10 +68,12 @@ extern Thread ThreadRingThread(Ring threadRing); extern Arena ThreadArena(Thread thread); -extern Res ThreadScan(ScanState ss, Thread thread, Word *stackCold, +extern Res ThreadScan(ScanState ss, Thread thread, void *stackCold, mps_area_scan_t scan_area, void *closure); +extern void ThreadSetup(void); + #endif /* th_h */ diff --git a/mps/code/than.c b/mps/code/than.c index 3e2dbaa81a0..6b13f4cf975 100644 --- a/mps/code/than.c +++ b/mps/code/than.c @@ -47,8 +47,7 @@ Res ThreadRegister(Thread *threadReturn, Arena arena) AVER(threadReturn != NULL); - res = ControlAlloc(&p, arena, sizeof(ThreadStruct), - /* withReservoirPermit */ FALSE); + res = ControlAlloc(&p, arena, sizeof(ThreadStruct)); if (res != ResOK) return res; thread = (Thread)p; @@ -117,7 +116,7 @@ Arena ThreadArena(Thread thread) } -Res ThreadScan(ScanState ss, Thread thread, Word *stackCold, +Res ThreadScan(ScanState ss, Thread thread, void *stackCold, mps_area_scan_t scan_area, void *closure) { @@ -143,6 +142,12 @@ Res ThreadDescribe(Thread thread, mps_lib_FILE *stream, Count depth) } +void ThreadSetup(void) +{ + /* Nothing to do as ANSI platform does not have fork(). */ +} + + /* C. COPYRIGHT AND LICENSE * * Copyright (C) 2001-2014 Ravenbrook Limited . diff --git a/mps/code/thix.c b/mps/code/thix.c index 219ae33bcdd..40555392075 100644 --- a/mps/code/thix.c +++ b/mps/code/thix.c @@ -1,7 +1,7 @@ /* thix.c: Threads Manager for Posix threads * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * * .purpose: This is a pthreads implementation of the threads manager. * This implements . @@ -32,12 +32,17 @@ * word-aligned at the time of reading the context of another thread. */ -#include "prmcix.h" #include "mpm.h" -#include +#if !defined(MPS_OS_FR) && !defined(MPS_OS_LI) +#error "thix.c is specific to MPS_OS_FR or MPS_OS_LI" +#endif + +#include "prmcix.h" #include "pthrdext.h" +#include + SRCID(thix, "$Id$"); @@ -51,7 +56,7 @@ typedef struct mps_thr_s { /* PThreads thread structure */ Bool alive; /* thread believed to be alive? */ PThreadextStruct thrextStruct; /* PThreads extension */ pthread_t id; /* Pthread object of thread */ - MutatorFaultContext mfc; /* Context if suspended, NULL if not */ + MutatorContext context; /* Context if suspended, NULL if not */ } ThreadStruct; @@ -86,8 +91,7 @@ Res ThreadRegister(Thread *threadReturn, Arena arena) AVER(threadReturn != NULL); AVERT(Arena, arena); - res = ControlAlloc(&p, arena, sizeof(ThreadStruct), - /* withReservoirPermit */ FALSE); + res = ControlAlloc(&p, arena, sizeof(ThreadStruct)); if(res != ResOK) return res; thread = (Thread)p; @@ -101,7 +105,7 @@ Res ThreadRegister(Thread *threadReturn, Arena arena) ++arena->threadSerial; thread->arena = arena; thread->alive = TRUE; - thread->mfc = NULL; + thread->context = NULL; PThreadextInit(&thread->thrextStruct, thread->id); @@ -134,30 +138,26 @@ void ThreadDeregister(Thread thread, Arena arena) /* mapThreadRing -- map over threads on ring calling a function on - * each one except the current thread. + * each one. * * Threads that are found to be dead (that is, if func returns FALSE) - * are moved to deadRing, in order to implement + * are marked as dead and moved to deadRing, in order to implement * design.thread-manager.sol.thread.term.attempt. */ 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); AVER(thread->alive); - if (!pthread_equal(self, thread->id) /* .thread.id */ - && !(*func)(thread)) - { + if (!(*func)(thread)) { thread->alive = FALSE; RingRemove(&thread->arenaRing); RingAppend(deadRing, &thread->arenaRing); @@ -172,13 +172,18 @@ static void mapThreadRing(Ring threadRing, Ring deadRing, Bool (*func)(Thread)) static Bool threadSuspend(Thread thread) { + Res res; + pthread_t self; + self = pthread_self(); + if (pthread_equal(self, thread->id)) /* .thread.id */ + return TRUE; + /* .error.suspend: if PThreadextSuspend fails, we assume the thread * has been terminated. */ - Res res; - AVER(thread->mfc == NULL); - res = PThreadextSuspend(&thread->thrextStruct, &thread->mfc); + AVER(thread->context == NULL); + res = PThreadextSuspend(&thread->thrextStruct, &thread->context); AVER(res == ResOK); - AVER(thread->mfc != NULL); + AVER(thread->context != NULL); /* design.thread-manager.sol.thread.term.attempt */ return res == ResOK; } @@ -197,12 +202,17 @@ void ThreadRingSuspend(Ring threadRing, Ring deadRing) static Bool threadResume(Thread thread) { Res res; + pthread_t self; + self = pthread_self(); + if (pthread_equal(self, thread->id)) /* .thread.id */ + return TRUE; + /* .error.resume: If PThreadextResume fails, we assume the thread * has been terminated. */ - AVER(thread->mfc != NULL); + AVER(thread->context != NULL); res = PThreadextResume(&thread->thrextStruct); AVER(res == ResOK); - thread->mfc = NULL; + thread->context = NULL; /* design.thread-manager.sol.thread.term.attempt */ return res == ResOK; } @@ -239,7 +249,7 @@ Arena ThreadArena(Thread thread) /* ThreadScan -- scan the state of a thread (stack and regs) */ -Res ThreadScan(ScanState ss, Thread thread, Word *stackCold, +Res ThreadScan(ScanState ss, Thread thread, void *stackCold, mps_area_scan_t scan_area, void *closure) { @@ -255,14 +265,14 @@ Res ThreadScan(ScanState ss, Thread thread, Word *stackCold, if(res != ResOK) return res; } else if (thread->alive) { - MutatorFaultContext mfc; + MutatorContext context; Word *stackBase, *stackLimit; Addr stackPtr; - mfc = thread->mfc; - AVER(mfc != NULL); + context = thread->context; + AVER(context != NULL); - stackPtr = MutatorFaultContextSP(mfc); + stackPtr = MutatorContextSP(context); /* .stack.align */ stackBase = (Word *)AddrAlignUp(stackPtr, sizeof(Word)); stackLimit = stackCold; @@ -277,8 +287,8 @@ Res ThreadScan(ScanState ss, Thread thread, Word *stackCold, if(res != ResOK) return res; - /* scan the registers in the mutator fault context */ - res = MutatorFaultContextScan(ss, mfc, scan_area, closure); + /* scan the registers in the mutator context */ + res = MutatorContextScan(ss, context, scan_area, closure); if(res != ResOK) return res; } @@ -308,9 +318,36 @@ Res ThreadDescribe(Thread thread, mps_lib_FILE *stream, Count depth) } +/* threadAtForkChild -- for each arena, move threads except for the + * current thread to the dead ring . + */ + +static Bool threadForkChild(Thread thread) +{ + AVERT(Thread, thread); + return pthread_equal(pthread_self(), thread->id); /* .thread.id */ +} + +static void threadRingForkChild(Arena arena) +{ + AVERT(Arena, arena); + mapThreadRing(ArenaThreadRing(arena), ArenaDeadRing(arena), threadForkChild); +} + +static void threadAtForkChild(void) +{ + GlobalsArenaMap(threadRingForkChild); +} + +void ThreadSetup(void) +{ + pthread_atfork(NULL, NULL, threadAtForkChild); +} + + /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/thw3.c b/mps/code/thw3.c index b8b8b106680..980c323d674 100644 --- a/mps/code/thw3.c +++ b/mps/code/thw3.c @@ -1,12 +1,10 @@ -/* thw3i3.c: WIN32 THREAD MANAGER +/* thw3.c: WIN32 THREAD MANAGER * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * - * Implements thread registration, suspension, and stack - * scanning. See . - * - * This supports the along with or + * Implements thread registration, suspension, and stack and register + * scanning. See . * * .thread.id: The thread id is used to identify the current thread. * .thread.handle: The thread handle needs the enough access to @@ -28,6 +26,19 @@ * .error.suspend: SuspendThread is assumed to succeed unless the thread * has been destroyed. * + * .stack.full-descend: assumes full descending stack, that is, stack + * pointer points to the last allocated location and stack grows + * downwards. + * + * .stack.below-bottom: it's legal for the stack pointer to be at a + * higher address than the registered bottom of stack. This might + * happen if the stack of another thread doesn't contain any frames + * belonging to the client language. In this case, the stack should + * not be scanned. + * + * .stack.align: assume roots on the stack are always word-aligned, + * but don't assume that the stack pointer is necessarily word-aligned + * at the time of reading the context of another thread. * * .nt: uses Win32 specific stuff * HANDLE @@ -39,22 +50,32 @@ * CloseHandle * SuspendThread * ResumeThread - * */ #include "mpm.h" #if !defined(MPS_OS_W3) /* .nt */ -#error "Compiling thw3 when MPS_OS_W3 not defined." +#error "thw3.c is specific to MPS_OS_W3" #endif -#include "thw3.h" - +#include "prmcw3.h" #include "mpswin.h" SRCID(thw3, "$Id$"); +typedef struct mps_thr_s { /* Win32 thread structure */ + Sig sig; /* */ + 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 */ +} ThreadStruct; + + Bool ThreadCheck(Thread thread) { CHECKS(Thread, thread); @@ -83,8 +104,7 @@ Res ThreadRegister(Thread *threadReturn, Arena arena) AVER(threadReturn != NULL); AVERT(Arena, arena); - res = ControlAlloc(&p, arena, sizeof(ThreadStruct), - /* withReservoirPermit */ FALSE); + res = ControlAlloc(&p, arena, sizeof(ThreadStruct)); if(res != ResOK) return res; thread = (Thread)p; /* avoid pun */ @@ -239,9 +259,68 @@ Res ThreadDescribe(Thread thread, mps_lib_FILE *stream, Count depth) } +Res ThreadScan(ScanState ss, Thread thread, Word *stackCold, + mps_area_scan_t scan_area, void *closure) +{ + DWORD id; + Res res; + + id = GetCurrentThreadId(); + + if (id != thread->id) { /* .thread.id */ + MutatorContextStruct context; + Word *stackBase, *stackLimit; + Addr stackPtr; + + /* scan stack and register roots in other threads */ + /* .thread.handle.get-context */ + res = MutatorContextInitThread(&context, thread->handle); + if (res != ResOK) { + /* .error.get-context */ + /* We assume that the thread must have been destroyed. */ + /* We ignore the situation by returning immediately. */ + return ResOK; + } + + stackPtr = MutatorContextSP(&context); + /* .stack.align */ + stackBase = (Word *)AddrAlignUp(stackPtr, sizeof(Word)); + stackLimit = stackCold; + if (stackBase >= stackLimit) + return ResOK; /* .stack.below-bottom */ + + /* scan stack inclusive of current sp and exclusive of + * stackCold (.stack.full-descend) + */ + res = TraceScanArea(ss, stackBase, stackLimit, + scan_area, closure); + if (res != ResOK) + return res; + + /* Scan registers. */ + res = MutatorContextScan(ss, &context, scan_area, closure); + if (res != ResOK) + return res; + + } else { /* scan this thread's stack */ + res = StackScan(ss, stackCold, scan_area, closure); + if (res != ResOK) + return res; + } + + return ResOK; +} + + +void ThreadSetup(void) +{ + /* Nothing to do as MPS does not support fork() on Windows. */ +} + + /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/thw3i3.c b/mps/code/thw3i3.c deleted file mode 100644 index 03675ed46b2..00000000000 --- a/mps/code/thw3i3.c +++ /dev/null @@ -1,172 +0,0 @@ -/* thw3i3.c: WIN32 THREAD MANAGER x86 - * - * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. - * - * Implements thread stack scanning. See . - * - * This supports the together with - * - * .thread.id: The thread id is used to identify the current thread. - * - * - * ASSUMPTIONS - * - * .error: some major errors are assumed not to happen. - * - * Other errors are assumed to only happen in certain circumstances. - * .error.get-context: GetThreadContext is assumed to succeed unless the - * thread has been destroyed. - * - * .stack.full-descend: assumes full descending stack. - * i.e. stack pointer points to the last allocated location; - * stack grows downwards. - * - * .stack.below-bottom: it's legal for the stack pointer to be at a - * higher address than the registered bottom of stack. This might - * happen if the stack of another thread doesn't contain any frames - * belonging to the client language. In this case, the stack should - * not be scanned. - * - * .stack.align: assume roots on the stack are always word-aligned, - * but don't assume that the stack pointer is necessarily - * word-aligned at the time of reading the context of another thread. - * - * .i3: assumes MPS_ARCH_I3 - * .i3.sp: The sp in the context is Esp - * .i3.context: Esp is in control context so .context.sp holds - * The root registers are Edi, Esi, Ebx, Edx, Ecx, Eax - * these are given by CONTEXT_INTEGER, so .context.regroots holds. - * - * .nt: uses Win32 specific stuff - * HANDLE - * DWORD - * GetCurrentThreadId - * CONTEXT - * CONTEXT_CONTROL | CONTEXT_INTEGER - * GetThreadContext - * - * .context: ContextFlags determine what is recorded by - * GetThreadContext. This should be set to whichever bits of the - * context that need to be recorded. This should include: - * .context.sp: sp assumed to be recorded by CONTEXT_CONTROL. - * .context.regroots: assumed to be recorded by CONTEXT_INTEGER. - * see winnt.h for description of CONTEXT and ContextFlags. - */ - -#include "mpm.h" - -#if !defined(MPS_OS_W3) || !defined(MPS_ARCH_I3) /* .i3 .nt */ -#error "Compiling thw3i3 when MPS_OS_W3 or MPS_ARCH_I3 not defined." -#endif - -#include "thw3.h" - -#include "mpswin.h" - -SRCID(thw3i3, "$Id$"); - - -Res ThreadScan(ScanState ss, Thread thread, Word *stackCold, - mps_area_scan_t scan_area, void *closure) -{ - DWORD id; - Res res; - - id = GetCurrentThreadId(); - - if (id != thread->id) { /* .thread.id */ - CONTEXT context; - BOOL success; - Word *stackBase, *stackLimit; - Addr stackPtr; - - /* scan stack and register roots in other threads */ - - /* This dumps the relevant registers into the context */ - /* .context.flags */ - context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; - /* .thread.handle.get-context */ - success = GetThreadContext(thread->handle, &context); - if(!success) { - /* .error.get-context */ - /* We assume that the thread must have been destroyed. */ - /* We ignore the situation by returning immediately. */ - return ResOK; - } - - stackPtr = (Addr)context.Esp; /* .i3.sp */ - /* .stack.align */ - stackBase = (Word *)AddrAlignUp(stackPtr, sizeof(Word)); - stackLimit = stackCold; - if (stackBase >= stackLimit) - return ResOK; /* .stack.below-bottom */ - - /* scan stack inclusive of current sp and exclusive of - * stackCold (.stack.full-descend) - */ - res = TraceScanArea(ss, stackBase, stackLimit, - scan_area, closure); - if(res != ResOK) - return res; - - /* (.context.regroots) - * This scans the root registers (.context.regroots). It also - * unnecessarily scans the rest of the context. The optimisation - * to scan only relevant parts would be machine dependent. - */ - res = TraceScanArea(ss, (Word *)&context, - (Word *)((char *)&context + sizeof(CONTEXT)), - scan_area, closure); - if(res != ResOK) - return res; - - } else { /* scan this thread's stack */ - res = StackScan(ss, stackCold, scan_area, closure); - if(res != ResOK) - return res; - } - - return ResOK; -} - -/* C. COPYRIGHT AND LICENSE - * - * Copyright (C) 2001-2002 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/mps/code/thw3i6.c b/mps/code/thw3i6.c deleted file mode 100644 index 67350022ad6..00000000000 --- a/mps/code/thw3i6.c +++ /dev/null @@ -1,173 +0,0 @@ -/* thw3i3.c: WIN32 THREAD MANAGER x86 - * - * $Id$ - * Copyright (c) 2001 Ravenbrook Limited. See end of file for license. - * - * Implements thread stack scanning. See . - * - * This supports the together with - * - * .thread.id: The thread id is used to identify the current thread. - * - * - * ASSUMPTIONS - * - * .error: some major errors are assumed not to happen. - * - * Other errors are assumed to only happen in certain circumstances. - * .error.get-context: GetThreadContext is assumed to succeed unless the - * thread has been destroyed. - * - * .stack.full-descend: assumes full descending stack. - * i.e. stack pointer points to the last allocated location; - * stack grows downwards. - * - * .stack.below-bottom: it's legal for the stack pointer to be at a - * higher address than the registered bottom of stack. This might - * happen if the stack of another thread doesn't contain any frames - * belonging to the client language. In this case, the stack should - * not be scanned. - * - * .stack.align: assume roots on the stack are always word-aligned, - * but don't assume that the stack pointer is necessarily - * word-aligned at the time of reading the context of another thread. - * - * .i6: assumes MPS_ARCH_I6 - * .i6.sp: The sp in the context is Rsp - * .i6.context: Rsp is in control context so .context.sp holds - * The root registers are Rdi, Rsi, Rbx, Rbp, Rdx, Rcx, Rax, R8 - R15 - * these are given by CONTEXT_INTEGER, so .context.regroots holds. - * - * .nt: uses Win32 specific stuff - * HANDLE - * DWORD - * GetCurrentThreadId - * CONTEXT - * CONTEXT_CONTROL | CONTEXT_INTEGER - * GetThreadContext - * - * .context: ContextFlags determine what is recorded by - * GetThreadContext. This should be set to whichever bits of the - * context that need to be recorded. This should include: - * .context.sp: sp assumed to be recorded by CONTEXT_CONTROL. - * .context.regroots: assumed to be recorded by CONTEXT_INTEGER. - * see winnt.h for description of CONTEXT and ContextFlags. - */ - -#include "mpm.h" - -#if !defined(MPS_OS_W3) || !defined(MPS_ARCH_I6) /* .i6 .nt */ -#error "Compiling thw3i6 when MPS_OS_W3 or MPS_ARCH_I6 not defined." -#endif - -#include "thw3.h" - -#include "mpswin.h" - -SRCID(thw3i6, "$Id$"); - - -Res ThreadScan(ScanState ss, Thread thread, Word *stackCold, - mps_area_scan_t scan_area, - void *closure) -{ - DWORD id; - Res res; - - id = GetCurrentThreadId(); - - if (id != thread->id) { /* .thread.id */ - CONTEXT context; - BOOL success; - Word *stackBase, *stackLimit; - Addr stackPtr; - - /* scan stack and register roots in other threads */ - - /* This dumps the relevant registers into the context */ - /* .context.flags */ - context.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER; - /* .thread.handle.get-context */ - success = GetThreadContext(thread->handle, &context); - if(!success) { - /* .error.get-context */ - /* We assume that the thread must have been destroyed. */ - /* We ignore the situation by returning immediately. */ - return ResOK; - } - - stackPtr = (Addr)context.Rsp; /* .i6.sp */ - /* .stack.align */ - stackBase = (Word *)AddrAlignUp(stackPtr, sizeof(Word)); - stackLimit = stackCold; - if (stackBase >= stackLimit) - return ResOK; /* .stack.below-bottom */ - - /* scan stack inclusive of current sp and exclusive of - * stackCold (.stack.full-descend) - */ - res = TraceScanArea(ss, stackBase, stackLimit, - scan_area, closure); - if(res != ResOK) - return res; - - /* (.context.regroots) - * This scans the root registers (.context.regroots). It also - * unnecessarily scans the rest of the context. The optimisation - * to scan only relevant parts would be machine dependent. - */ - res = TraceScanArea(ss, (Word *)&context, - (Word *)((char *)&context + sizeof(CONTEXT)), - scan_area, closure); - if(res != ResOK) - return res; - - } else { /* scan this thread's stack */ - res = StackScan(ss, stackCold, scan_area, closure); - if(res != ResOK) - return res; - } - - return ResOK; -} - -/* C. COPYRIGHT AND LICENSE - * - * Copyright (C) 2001-2002 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/mps/code/thxc.c b/mps/code/thxc.c index 3bb449d46fe..fb45f35a09f 100644 --- a/mps/code/thxc.c +++ b/mps/code/thxc.c @@ -1,7 +1,7 @@ -/* thxc.c: OS X MACH THREADS MANAGER +/* thxc.c: THREAD MANAGER (macOS) * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * * .design: See . * @@ -20,23 +20,30 @@ */ #include "mpm.h" + +#if !defined(MPS_OS_XC) +#error "protw3.c is specific to MPS_OS_XC" +#endif + #include "protxc.h" #include #include #include #include +#include SRCID(thxc, "$Id$"); -typedef struct mps_thr_s { /* OS X / Mach thread structure */ +typedef struct mps_thr_s { /* macOS thread structure */ Sig sig; /* */ Serial serial; /* from arena->threadSerial */ Arena arena; /* owning arena */ RingStruct arenaRing; /* attaches to arena */ Bool alive; /* thread believed to be alive? */ + Bool forking; /* thread currently calling fork? */ thread_port_t port; /* thread kernel port */ } ThreadStruct; @@ -48,6 +55,7 @@ Bool ThreadCheck(Thread thread) CHECKL(thread->serial < thread->arena->threadSerial); CHECKD_NOSIG(Ring, &thread->arenaRing); CHECKL(BoolCheck(thread->alive)); + CHECKL(BoolCheck(thread->forking)); CHECKL(MACH_PORT_VALID(thread->port)); return TRUE; } @@ -69,8 +77,7 @@ Res ThreadRegister(Thread *threadReturn, Arena arena) AVER(threadReturn != NULL); - res = ControlAlloc(&p, arena, sizeof(ThreadStruct), - /* withReservoirPermit */ FALSE); + res = ControlAlloc(&p, arena, sizeof(ThreadStruct)); if (res != ResOK) return res; thread = (Thread)p; @@ -81,11 +88,13 @@ Res ThreadRegister(Thread *threadReturn, Arena arena) thread->serial = arena->threadSerial; ++arena->threadSerial; thread->alive = TRUE; + thread->forking = FALSE; thread->port = mach_thread_self(); + AVER(MACH_PORT_VALID(thread->port)); thread->sig = ThreadSig; AVERT(Thread, thread); - ProtThreadRegister(FALSE); + ProtThreadRegister(); ring = ArenaThreadRing(arena); @@ -100,6 +109,7 @@ void ThreadDeregister(Thread thread, Arena arena) { AVERT(Thread, thread); AVERT(Arena, arena); + AVER(!thread->forking); RingRemove(&thread->arenaRing); @@ -112,7 +122,7 @@ void ThreadDeregister(Thread thread, Arena arena) /* mapThreadRing -- map over threads on ring calling a function on - * each one except the current thread. + * each one. * * 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 @@ -122,21 +132,16 @@ void ThreadDeregister(Thread thread, Arena arena) 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); AVER(thread->alive); - if (thread->port != self - && !(*func)(thread)) - { + if (!(*func)(thread)) { thread->alive = FALSE; RingRemove(&thread->arenaRing); RingAppend(deadRing, &thread->arenaRing); @@ -148,6 +153,11 @@ static void mapThreadRing(Ring threadRing, Ring deadRing, Bool (*func)(Thread)) static Bool threadSuspend(Thread thread) { kern_return_t kern_return; + mach_port_t self = mach_thread_self(); + AVER(MACH_PORT_VALID(self)); + if (thread->port == self) + return TRUE; + kern_return = thread_suspend(thread->port); /* No rendezvous is necessary: thread_suspend "prevents the thread * from executing any more user-level instructions" */ @@ -158,18 +168,6 @@ static Bool threadSuspend(Thread thread) return kern_return == KERN_SUCCESS; } -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. @@ -179,6 +177,24 @@ void ThreadRingSuspend(Ring threadRing, Ring deadRing) mapThreadRing(threadRing, deadRing, threadSuspend); } + +static Bool threadResume(Thread thread) +{ + kern_return_t kern_return; + mach_port_t self = mach_thread_self(); + AVER(MACH_PORT_VALID(self)); + if (thread->port == self) + return TRUE; + + 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; +} + /* ThreadRingResume -- resume all threads on a ring, except the * current one. */ @@ -187,6 +203,7 @@ void ThreadRingResume(Ring threadRing, Ring deadRing) mapThreadRing(threadRing, deadRing, threadResume); } + Thread ThreadRingThread(Ring threadRing) { Thread thread; @@ -210,7 +227,7 @@ Arena ThreadArena(Thread thread) #include "prmcxc.h" -Res ThreadScan(ScanState ss, Thread thread, Word *stackCold, +Res ThreadScan(ScanState ss, Thread thread, void *stackCold, mps_area_scan_t scan_area, void *closure) { mach_port_t self; @@ -226,7 +243,7 @@ Res ThreadScan(ScanState ss, Thread thread, Word *stackCold, if(res != ResOK) return res; } else if (thread->alive) { - MutatorFaultContextStruct mfcStruct; + MutatorContextStruct context; THREAD_STATE_S threadState; Word *stackBase, *stackLimit; Addr stackPtr; @@ -237,19 +254,18 @@ Res ThreadScan(ScanState ss, Thread thread, Word *stackCold, order to assert that the thread is suspended, but it's probably unnecessary and is a lot of work to check a static condition. */ - mfcStruct.address = NULL; - mfcStruct.threadState = &threadState; + MutatorContextInitThread(&context, &threadState); count = THREAD_STATE_COUNT; - AVER(sizeof(*mfcStruct.threadState) == count * sizeof(natural_t)); + AVER(sizeof(*context.threadState) == count * sizeof(natural_t)); kern_return = thread_get_state(thread->port, THREAD_STATE_FLAVOR, - (thread_state_t)mfcStruct.threadState, + (thread_state_t)context.threadState, &count); AVER(kern_return == KERN_SUCCESS); AVER(count == THREAD_STATE_COUNT); - stackPtr = MutatorFaultContextSP(&mfcStruct); + stackPtr = MutatorContextSP(&context); /* .stack.align */ stackBase = (Word *)AddrAlignUp(stackPtr, sizeof(Word)); stackLimit = stackCold; @@ -264,8 +280,8 @@ Res ThreadScan(ScanState ss, Thread thread, Word *stackCold, if(res != ResOK) return res; - /* scan the registers in the mutator fault context */ - res = MutatorFaultContextScan(ss, &mfcStruct, scan_area, closure); + /* scan the registers in the mutator context */ + res = MutatorContextScan(ss, &context, scan_area, closure); if(res != ResOK) return res; } @@ -293,9 +309,95 @@ Res ThreadDescribe(Thread thread, mps_lib_FILE *stream, Count depth) } +/* threadAtForkPrepare -- for each arena, mark the current thread as + * forking . + */ + +static Bool threadForkPrepare(Thread thread) +{ + mach_port_t self; + AVERT(Thread, thread); + AVER(!thread->forking); + self = mach_thread_self(); + AVER(MACH_PORT_VALID(self)); + thread->forking = (thread->port == self); + return TRUE; +} + +static void threadRingForkPrepare(Arena arena) +{ + AVERT(Arena, arena); + mapThreadRing(ArenaThreadRing(arena), ArenaDeadRing(arena), threadForkPrepare); +} + +static void threadAtForkPrepare(void) +{ + GlobalsArenaMap(threadRingForkPrepare); +} + + +/* threadAtForkParent -- for each arena, clear the forking flag for + * all threads . + */ + +static Bool threadForkParent(Thread thread) +{ + AVERT(Thread, thread); + thread->forking = FALSE; + return TRUE; +} + +static void threadRingForkParent(Arena arena) +{ + AVERT(Arena, arena); + mapThreadRing(ArenaThreadRing(arena), ArenaDeadRing(arena), threadForkParent); +} + +static void threadAtForkParent(void) +{ + GlobalsArenaMap(threadRingForkParent); +} + + +/* threadAtForkChild -- For each arena, move all threads to the dead + * ring, except for the thread that was marked as forking by the + * prepare handler , for which + * update its mach port . + */ + +static Bool threadForkChild(Thread thread) +{ + AVERT(Thread, thread); + if (thread->forking) { + thread->port = mach_thread_self(); + AVER(MACH_PORT_VALID(thread->port)); + thread->forking = FALSE; + return TRUE; + } else { + return FALSE; + } +} + +static void threadRingForkChild(Arena arena) +{ + AVERT(Arena, arena); + mapThreadRing(ArenaThreadRing(arena), ArenaDeadRing(arena), threadForkChild); +} + +static void threadAtForkChild(void) +{ + GlobalsArenaMap(threadRingForkChild); +} + +void ThreadSetup(void) +{ + pthread_atfork(threadAtForkPrepare, threadAtForkParent, threadAtForkChild); +} + + /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/trace.c b/mps/code/trace.c index 5ce3e3617ad..da390812bb6 100644 --- a/mps/code/trace.c +++ b/mps/code/trace.c @@ -1,7 +1,7 @@ /* trace.c: GENERIC TRACER IMPLEMENTATION * * $Id$ - * Copyright (c) 2001-2016 Ravenbrook Limited. + * Copyright (c) 2001-2018 Ravenbrook Limited. * See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. * @@ -69,10 +69,10 @@ void ScanStateInit(ScanState ss, TraceSet ts, Arena arena, AVERT(Rank, rank); /* white is arbitrary and can't be checked */ - /* NOTE: We can only currently support scanning for a set of traces with - the same fix method and closure. To remove this restriction, - it would be necessary to dispatch to the fix methods of sets of traces - in TraceFix. */ + /* NOTE: We can only currently support scanning for a set of traces + with the same fix method. To remove this restriction, it would be + necessary to dispatch to the fix methods of sets of traces in + TraceFix. */ ss->fix = NULL; ss->fixClosure = NULL; TRACE_SET_ITER(ti, trace, ts, arena) { @@ -88,8 +88,8 @@ void ScanStateInit(ScanState ss, TraceSet ts, Arena arena, /* If the fix method is the normal GC fix, then we optimise the test for whether it's an emergency or not by updating the dispatch here, once. */ - if (ss->fix == PoolFix && ArenaEmergency(arena)) - ss->fix = PoolFixEmergency; + if (ss->fix == SegFix && ArenaEmergency(arena)) + ss->fix = SegFixEmergency; ss->rank = rank; ss->traces = ts; @@ -105,11 +105,9 @@ void ScanStateInit(ScanState ss, TraceSet ts, Arena arena, STATISTIC(ss->nailCount = (Count)0); STATISTIC(ss->snapCount = (Count)0); STATISTIC(ss->forwardedCount = (Count)0); - ss->forwardedSize = (Size)0; /* see .message.data */ STATISTIC(ss->preservedInPlaceCount = (Count)0); - ss->preservedInPlaceSize = (Size)0; /* see .message.data */ STATISTIC(ss->copiedSize = (Size)0); - ss->scannedSize = (Size)0; /* see .workclock */ + ss->scannedSize = (Size)0; /* see .work */ ss->sig = ScanStateSig; AVERT(ScanState, ss); @@ -155,23 +153,29 @@ Bool TraceCheck(Trace trace) CHECKL(trace == &trace->arena->trace[trace->ti]); CHECKL(TraceSetIsMember(trace->arena->busyTraces, trace)); CHECKL(ZoneSetSub(trace->mayMove, trace->white)); + CHECKD_NOSIG(Ring, &trace->genRing); /* Use trace->state to check more invariants. */ switch(trace->state) { case TraceINIT: + CHECKL(!TraceSetIsMember(trace->arena->flippedTraces, trace)); /* @@@@ What can be checked here? */ break; case TraceUNFLIPPED: + CHECKL(!RingIsSingle(&trace->genRing)); CHECKL(!TraceSetIsMember(trace->arena->flippedTraces, trace)); /* @@@@ Assert that mutator is grey for trace. */ break; case TraceFLIPPED: + CHECKL(!RingIsSingle(&trace->genRing)); CHECKL(TraceSetIsMember(trace->arena->flippedTraces, trace)); + CHECKL(RankCheck(trace->band)); /* @@@@ Assert that mutator is black for trace. */ break; case TraceRECLAIM: + CHECKL(!RingIsSingle(&trace->genRing)); CHECKL(TraceSetIsMember(trace->arena->flippedTraces, trace)); /* @@@@ Assert that grey set is empty for trace. */ break; @@ -185,13 +189,6 @@ Bool TraceCheck(Trace trace) NOTREACHED; break; } - /* Valid values for band depend on state. */ - if(trace->state == TraceFLIPPED) { - CHECKL(RankCheck(trace->band)); - } - if(trace->chain != NULL) { - CHECKU(Chain, trace->chain); - } CHECKL(FUNCHECK(trace->fix)); /* Can't check trace->fixClosure. */ @@ -271,13 +268,13 @@ static void traceUpdateCounts(Trace trace, ScanState ss, switch(phase) { case traceAccountingPhaseRootScan: { trace->rootScanSize += ss->scannedSize; - trace->rootCopiedSize += ss->copiedSize; + STATISTIC(trace->rootCopiedSize += ss->copiedSize); STATISTIC(++trace->rootScanCount); break; } case traceAccountingPhaseSegScan: { - trace->segScanSize += ss->scannedSize; /* see .workclock */ - trace->segCopiedSize += ss->copiedSize; + trace->segScanSize += ss->scannedSize; /* see .work */ + STATISTIC(trace->segCopiedSize += ss->copiedSize); STATISTIC(++trace->segScanCount); break; } @@ -295,11 +292,7 @@ static void traceUpdateCounts(Trace trace, ScanState ss, STATISTIC(trace->nailCount += ss->nailCount); STATISTIC(trace->snapCount += ss->snapCount); STATISTIC(trace->forwardedCount += ss->forwardedCount); - trace->forwardedSize += ss->forwardedSize; /* see .message.data */ STATISTIC(trace->preservedInPlaceCount += ss->preservedInPlaceCount); - trace->preservedInPlaceSize += ss->preservedInPlaceSize; - - return; } @@ -315,7 +308,6 @@ static void traceSetUpdateCounts(TraceSet ts, Arena arena, ScanState ss, TRACE_SET_ITER(ti, trace, ts, arena) traceUpdateCounts(trace, ss, phase); TRACE_SET_ITER_END(ti, trace, ts, arena); - return; } @@ -338,12 +330,27 @@ static ZoneSet traceSetWhiteUnion(TraceSet ts, Arena arena) } +/* TraceIsEmpty -- return TRUE if trace has no condemned segments + * + * .empty.size: If the trace has a condemned size of zero, then it has + * no white segments, because we don't allow pools to whiten segments + * with no white objects in. + */ + +Bool TraceIsEmpty(Trace trace) +{ + AVERT(Trace, trace); + return trace->condemned == 0; +} + + /* TraceAddWhite -- add a segment to the white set of a trace */ Res TraceAddWhite(Trace trace, Seg seg) { Res res; Pool pool; + Size condemnedBefore; AVERT(Trace, trace); AVERT(Seg, seg); @@ -352,18 +359,25 @@ Res TraceAddWhite(Trace trace, Seg seg) pool = SegPool(seg); AVERT(Pool, pool); + condemnedBefore = trace->condemned; + /* Give the pool the opportunity to turn the segment white. */ /* If it fails, unwind. */ - res = PoolWhiten(pool, trace, seg); + res = SegWhiten(seg, trace); if(res != ResOK) return res; - /* Add the segment to the approximation of the white set if the */ - /* pool made it white. */ - if(TraceSetIsMember(SegWhite(seg), trace)) { + if (TraceSetIsMember(SegWhite(seg), trace)) { + /* Pools must not condemn empty segments, otherwise we can't tell + when a trace is empty and safe to destroy. See .empty.size. */ + AVER(trace->condemned > condemnedBefore); + + /* Add the segment to the approximation of the white set if the + pool made it white. */ trace->white = ZoneSetUnion(trace->white, ZoneSetOfSeg(trace->arena, seg)); + /* if the pool is a moving GC, then condemned objects may move */ - if(PoolHasAttr(pool, AttrMOVINGGC)) { + if (PoolHasAttr(pool, AttrMOVINGGC)) { trace->mayMove = ZoneSetUnion(trace->mayMove, ZoneSetOfSeg(trace->arena, seg)); } @@ -373,64 +387,80 @@ Res TraceAddWhite(Trace trace, Seg seg) } -/* TraceCondemnZones -- condemn all objects in the given zones - * - * 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. - * - * @@@@ This function would be more efficient if there were a cheaper - * way to select the segments in a particular zone set. */ +/* TraceCondemnStart -- start selecting generations to condemn for a trace */ -Res TraceCondemnZones(Trace trace, ZoneSet condemnedSet) +void TraceCondemnStart(Trace trace) { - Seg seg; - Arena arena; - Res res; - Bool haveWhiteSegs = FALSE; - AVERT(Trace, trace); - AVER(condemnedSet != ZoneSetEMPTY); + AVER(trace->state == TraceINIT); + AVER(trace->white == ZoneSetEMPTY); + AVER(RingIsSingle(&trace->genRing)); +} + + +/* TraceCondemnEnd -- condemn segments for trace + * + * Condemn the segments in the generations that were selected since + * TraceCondemnStart, and compute the predicted mortality of the + * condemned memory. If successful, update *mortalityReturn and return + * ResOK. + * + * We suspend the mutator threads so that the PoolWhiten methods can + * calculate white sets without the mutator allocating in buffers + * under our feet. See request.dylan.160098 + * . + * + * TODO: Consider how to avoid this suspend in order to implement + * incremental condemn. + */ + +Res TraceCondemnEnd(double *mortalityReturn, Trace trace) +{ + Size casualtySize = 0; + Ring genNode, genNext; + Res res; + + AVER(mortalityReturn != NULL); + AVERT(Trace, trace); 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(PoolHasAttr(SegPool(seg), AttrGC) - && ZoneSetSuper(condemnedSet, ZoneSetOfSeg(arena, seg))) - { - res = TraceAddWhite(trace, seg); - if(res != ResOK) - goto failBegin; - haveWhiteSegs = TRUE; - } - } while (SegNext(&seg, arena, seg)); + ShieldHold(trace->arena); + RING_FOR(genNode, &trace->genRing, genNext) { + Size condemnedBefore, condemnedGen; + Ring segNode, segNext; + GenDesc gen = GenDescOfTraceRing(genNode, trace); + AVERT(GenDesc, gen); + condemnedBefore = trace->condemned; + RING_FOR(segNode, &gen->segRing, segNext) { + GCSeg gcseg = RING_ELT(GCSeg, genRing, segNode); + AVERC(GCSeg, gcseg); + res = TraceAddWhite(trace, &gcseg->segStruct); + if (res != ResOK) + goto failBegin; + } + AVER(trace->condemned >= condemnedBefore); + condemnedGen = trace->condemned - condemnedBefore; + casualtySize += (Size)(condemnedGen * gen->mortality); } + ShieldRelease(trace->arena); - EVENT3(TraceCondemnZones, trace, condemnedSet, trace->white); - - /* The trace's white set must be a subset of the condemned set */ - AVER(ZoneSetSuper(condemnedSet, trace->white)); + if (TraceIsEmpty(trace)) + return ResFAIL; + *mortalityReturn = (double)casualtySize / trace->condemned; return ResOK; failBegin: - AVER(!haveWhiteSegs); /* See .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(TraceIsEmpty(trace)); + ShieldRelease(trace->arena); return res; } @@ -565,7 +595,7 @@ static Res traceFlip(Trace trace) arena = trace->arena; rfc.arena = arena; - ShieldSuspend(arena); + ShieldHold(arena); AVER(trace->state == TraceUNFLIPPED); AVER(!TraceSetIsMember(arena->flippedTraces, trace)); @@ -604,15 +634,11 @@ static Res traceFlip(Trace trace) /* drj 2003-02-19) */ /* Now that the mutator is black we must prevent it from reading */ - /* 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. */ + /* grey objects so that it can't obtain white pointers. */ for(rank = RankMIN; rank < RankLIMIT; ++rank) RING_FOR(node, ArenaGreyRing(arena, rank), nextNode) { Seg seg = SegOfGreyRing(node); - if(TraceSetInter(SegGrey(seg), arena->flippedTraces) == TraceSetEMPTY - && TraceSetIsMember(SegGrey(seg), trace)) - ShieldRaise(arena, seg, AccessREAD); + SegFlip(seg, trace); } /* @@@@ When write barrier collection is implemented, this is where */ @@ -626,11 +652,11 @@ static Res traceFlip(Trace trace) EVENT2(TraceFlipEnd, trace, arena); - ShieldResume(arena); + ShieldRelease(arena); return ResOK; failRootFlip: - ShieldResume(arena); + ShieldRelease(arena); return res; } @@ -650,7 +676,8 @@ static Res traceFlip(Trace trace) * This code is written to be adaptable to allocating Trace objects * dynamically. */ -static void TraceCreatePoolGen(GenDesc gen) +ATTRIBUTE_UNUSED +static void traceCreatePoolGen(GenDesc gen) { Ring n, nn; RING_FOR(n, &gen->locusRing, nn) { @@ -686,22 +713,22 @@ Res TraceCreate(Trace *traceReturn, Arena arena, int why) trace->ti = ti; trace->state = TraceINIT; trace->band = RankMIN; - trace->fix = PoolFix; + trace->fix = SegFix; trace->fixClosure = NULL; - trace->chain = NULL; + RingInit(&trace->genRing); STATISTIC(trace->preTraceArenaReserved = ArenaReserved(arena)); trace->condemned = (Size)0; /* nothing condemned yet */ trace->notCondemned = (Size)0; trace->foundation = (Size)0; /* nothing grey yet */ - trace->rate = (Size)0; /* no scanning to be done yet */ + trace->quantumWork = (Work)0; /* computed in TraceStart */ STATISTIC(trace->greySegCount = (Count)0); STATISTIC(trace->greySegMax = (Count)0); STATISTIC(trace->rootScanCount = (Count)0); trace->rootScanSize = (Size)0; - trace->rootCopiedSize = (Size)0; + STATISTIC(trace->rootCopiedSize = (Size)0); STATISTIC(trace->segScanCount = (Count)0); - trace->segScanSize = (Size)0; /* see .workclock */ - trace->segCopiedSize = (Size)0; + trace->segScanSize = (Size)0; /* see .work */ + STATISTIC(trace->segCopiedSize = (Size)0); STATISTIC(trace->singleScanCount = (Count)0); STATISTIC(trace->singleScanSize = (Size)0); STATISTIC(trace->singleCopiedSize = (Size)0); @@ -724,14 +751,7 @@ Res TraceCreate(Trace *traceReturn, Arena arena, int why) EVENT3(TraceCreate, trace, arena, (EventFU)why); - /* We suspend the mutator threads so that the PoolWhiten methods */ - /* can calculate white sets without the mutator allocating in */ - /* buffers under our feet. */ - /* @@@@ This is a short-term fix for request.dylan.160098_. */ - /* .. _request.dylan.160098: https://info.ravenbrook.com/project/mps/import/2001-11-05/mmprevol/request/dylan/160098 */ - ShieldSuspend(arena); - - STATISTIC_STAT ({ + STATISTIC({ /* Iterate over all chains, all GenDescs within a chain, and all * PoolGens within a GenDesc. */ Ring node; @@ -742,12 +762,12 @@ Res TraceCreate(Trace *traceReturn, Arena arena, int why) Index i; for (i = 0; i < chain->genCount; ++i) { GenDesc gen = &chain->gens[i]; - TraceCreatePoolGen(gen); + traceCreatePoolGen(gen); } } /* Now do topgen GenDesc, and all PoolGens within it. */ - TraceCreatePoolGen(&arena->topGen); + traceCreatePoolGen(&arena->topGen); }); *traceReturn = trace; @@ -755,7 +775,50 @@ Res TraceCreate(Trace *traceReturn, Arena arena, int why) } -/* TraceDestroy -- destroy a trace object +/* traceDestroyCommon -- common functionality for TraceDestroy* */ + +static void traceDestroyCommon(Trace trace) +{ + Ring node, nextNode; + + RING_FOR(node, &trace->genRing, nextNode) { + GenDesc gen = GenDescOfTraceRing(node, trace); + GenDescEndTrace(gen, trace); + } + RingFinish(&trace->genRing); + + /* Ensure that address space is returned to the operating system for + * traces that don't have any condemned objects (there might be + * manually allocated objects that were freed). See job003999. */ + ArenaCompact(trace->arena, trace); + + EVENT1(TraceDestroy, trace); + + /* Hopefully the trace reclaimed some memory, so clear any emergency. + * Do this before removing the trace from busyTraces, to avoid + * violating . */ + ArenaSetEmergency(trace->arena, FALSE); + + trace->sig = SigInvalid; + trace->arena->busyTraces = TraceSetDel(trace->arena->busyTraces, trace); + trace->arena->flippedTraces = TraceSetDel(trace->arena->flippedTraces, trace); +} + + +/* TraceDestroyInit -- destroy a trace object in state INIT */ + +void TraceDestroyInit(Trace trace) +{ + AVERT(Trace, trace); + AVER(trace->state == TraceINIT); + AVER(trace->condemned == 0); + AVER(!TraceSetIsMember(trace->arena->flippedTraces, trace)); + + traceDestroyCommon(trace); +} + + +/* TraceDestroyFinished -- destroy a trace object in state FINISHED * * Finish and deallocate a Trace object, freeing up a TraceId. * @@ -764,51 +827,31 @@ Res TraceCreate(Trace *traceReturn, Arena arena, int why) * etc. would need to be reset to black. This also means the error * paths in this file don't work. @@@@ */ -void TraceDestroy(Trace trace) +void TraceDestroyFinished(Trace trace) { AVERT(Trace, trace); AVER(trace->state == TraceFINISHED); - if(trace->chain == NULL) { - Ring chainNode, nextChainNode; - - /* Notify all the chains. */ - RING_FOR(chainNode, &trace->arena->chainRing, nextChainNode) { - Chain chain = RING_ELT(Chain, chainRing, chainNode); - - ChainEndGC(chain, trace); - } - } else { - ChainEndGC(trace->chain, trace); - } - - STATISTIC_STAT(EVENT13 - (TraceStatScan, trace, - trace->rootScanCount, trace->rootScanSize, - trace->rootCopiedSize, - trace->segScanCount, trace->segScanSize, - trace->segCopiedSize, - trace->singleScanCount, trace->singleScanSize, - trace->singleCopiedSize, - trace->readBarrierHitCount, trace->greySegMax, - trace->pointlessScanCount)); - STATISTIC_STAT(EVENT10 - (TraceStatFix, trace, - trace->fixRefCount, trace->segRefCount, - trace->whiteSegRefCount, - trace->nailCount, trace->snapCount, - trace->forwardedCount, trace->forwardedSize, - trace->preservedInPlaceCount, - trace->preservedInPlaceSize)); - STATISTIC_STAT(EVENT3 - (TraceStatReclaim, trace, + STATISTIC(EVENT13(TraceStatScan, trace, + trace->rootScanCount, trace->rootScanSize, + trace->rootCopiedSize, + trace->segScanCount, trace->segScanSize, + trace->segCopiedSize, + trace->singleScanCount, trace->singleScanSize, + trace->singleCopiedSize, + trace->readBarrierHitCount, trace->greySegMax, + trace->pointlessScanCount)); + STATISTIC(EVENT10(TraceStatFix, trace, + trace->fixRefCount, trace->segRefCount, + trace->whiteSegRefCount, + trace->nailCount, trace->snapCount, + trace->forwardedCount, trace->forwardedSize, + trace->preservedInPlaceCount, + trace->preservedInPlaceSize)); + STATISTIC(EVENT3(TraceStatReclaim, trace, trace->reclaimCount, trace->reclaimSize)); - EVENT1(TraceDestroy, trace); - - trace->sig = SigInvalid; - trace->arena->busyTraces = TraceSetDel(trace->arena->busyTraces, trace); - trace->arena->flippedTraces = TraceSetDel(trace->arena->flippedTraces, trace); + traceDestroyCommon(trace); } @@ -817,28 +860,27 @@ void TraceDestroy(Trace trace) static void traceReclaim(Trace trace) { Arena arena; - Seg seg; - Ring node, nextNode; + Ring genNode, genNext; 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)); + arena = trace->arena; + RING_FOR(genNode, &trace->genRing, genNext) { + Ring segNode, segNext; + GenDesc gen = GenDescOfTraceRing(genNode, trace); + AVERT(GenDesc, gen); + RING_FOR(segNode, &gen->segRing, segNext) { + GCSeg gcseg = RING_ELT(GCSeg, genRing, segNode); + Seg seg = &gcseg->segStruct; /* There shouldn't be any grey stuff left for this trace. */ AVER_CRITICAL(!TraceSetIsMember(SegGrey(seg), trace)); - - if(TraceSetIsMember(SegWhite(seg), trace)) { - AVER_CRITICAL(PoolHasAttr(pool, AttrGC)); + if (TraceSetIsMember(SegWhite(seg), trace)) { + Addr base = SegBase(seg); + AVER_CRITICAL(PoolHasAttr(SegPool(seg), AttrGC)); STATISTIC(++trace->reclaimCount); - PoolReclaim(pool, trace, seg); + SegReclaim(seg, trace); /* If the segment still exists, it should no longer be white. */ /* Note that the seg returned by this SegOfAddr may not be */ @@ -849,22 +891,16 @@ static void traceReclaim(Trace trace) /* 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))); + AVER_CRITICAL(!SegOfAddr(&nonWhiteSeg, arena, base) + || !TraceSetIsMember(SegWhite(nonWhiteSeg), trace)); UNUSED(nonWhiteSeg); /* */ } } - } while(SegNextOfRing(&seg, arena, pool, next)); + } } trace->state = TraceFINISHED; - /* Call each pool's TraceEnd method -- do end-of-trace work */ - RING_FOR(node, &ArenaGlobals(arena)->poolRing, nextNode) { - Pool pool = RING_ELT(Pool, arenaRing, node); - PoolTraceEnd(pool, trace); - } - ArenaCompact(arena, trace); /* let arenavm drop chunks */ TracePostMessage(trace); /* trace end */ @@ -1089,6 +1125,7 @@ static Res traceScanSegRes(TraceSet ts, Rank rank, Arena arena, Seg seg) Bool wasTotal; ZoneSet white; Res res; + RefSet summary; /* The reason for scanning a segment is that it's grey. */ AVER(TraceSetInter(ts, SegGrey(seg)) != TraceSetEMPTY); @@ -1098,7 +1135,7 @@ static Res traceScanSegRes(TraceSet ts, Rank rank, Arena arena, Seg seg) /* Only scan a segment if it refers to the white set. */ if(ZoneSetInter(white, SegSummary(seg)) == ZoneSetEMPTY) { - PoolBlacken(SegPool(seg), ts, seg); + SegBlacken(seg, ts); /* Setup result code to return later. */ res = ResOK; } else { /* scan it */ @@ -1108,43 +1145,58 @@ static Res traceScanSegRes(TraceSet ts, Rank rank, Arena arena, Seg seg) /* Expose the segment to make sure we can scan it. */ ShieldExpose(arena, seg); - res = PoolScan(&wasTotal, ss, SegPool(seg), seg); + res = SegScan(&wasTotal, seg, ss); /* Cover, regardless of result */ ShieldCover(arena, seg); traceSetUpdateCounts(ts, arena, ss, traceAccountingPhaseSegScan); /* Count segments scanned pointlessly */ - STATISTIC_STAT - ({ - TraceId ti; Trace trace; - Count whiteSegRefCount = 0; + STATISTIC({ + TraceId ti; Trace trace; + Count whiteSegRefCount = 0; - TRACE_SET_ITER(ti, trace, ts, arena) - whiteSegRefCount += trace->whiteSegRefCount; - TRACE_SET_ITER_END(ti, trace, ts, arena); - if(whiteSegRefCount == 0) - TRACE_SET_ITER(ti, trace, ts, arena) - ++trace->pointlessScanCount; - TRACE_SET_ITER_END(ti, trace, ts, arena); - }); + TRACE_SET_ITER(ti, trace, ts, arena) + whiteSegRefCount += trace->whiteSegRefCount; + TRACE_SET_ITER_END(ti, trace, ts, arena); + if(whiteSegRefCount == 0) + TRACE_SET_ITER(ti, trace, ts, arena) + ++trace->pointlessScanCount; + TRACE_SET_ITER_END(ti, trace, ts, arena); + }); /* Following is true whether or not scan was total. */ /* See . */ /* .verify.segsummary: were the seg contents, as found by this * scan, consistent with the recorded SegSummary? */ - AVER(RefSetSub(ScanStateUnfixedSummary(ss), SegSummary(seg))); + AVER(RefSetSub(ScanStateUnfixedSummary(ss), SegSummary(seg))); /* */ - if(res != ResOK || !wasTotal) { - /* scan was partial, so... */ - /* scanned summary should be ORed into segment summary. */ - SegSetSummary(seg, RefSetUnion(SegSummary(seg), ScanStateSummary(ss))); + /* Write barrier deferral -- see design.mps.write-barrier.deferral. */ + /* Did the segment refer to the white set? */ + if (ZoneSetInter(ScanStateUnfixedSummary(ss), white) == ZoneSetEMPTY) { + /* Boring scan. One step closer to raising the write barrier. */ + if (seg->defer > 0) + --seg->defer; } else { - /* all objects on segment have been scanned, so... */ - /* scanned summary should replace the segment summary. */ - SegSetSummary(seg, ScanStateSummary(ss)); + /* Interesting scan. Defer raising the write barrier. */ + if (seg->defer < WB_DEFER_DELAY) + seg->defer = WB_DEFER_DELAY; } + /* Only apply the write barrier if it is not deferred. */ + if (seg->defer == 0) { + /* If we scanned every reference in the segment then we have a + complete summary we can set. Otherwise, we just have + information about more zones that the segment refers to. */ + if (res == ResOK && wasTotal) + summary = ScanStateSummary(ss); + else + summary = RefSetUnion(SegSummary(seg), ScanStateSummary(ss)); + } else { + summary = RefSetUNIV; + } + SegSetSummary(seg, summary); + ScanStateFinish(ss); } @@ -1185,26 +1237,34 @@ static Res traceScanSeg(TraceSet ts, Rank rank, Arena arena, Seg seg) void TraceSegAccess(Arena arena, Seg seg, AccessSet mode) { Res res; + AccessSet shieldHit; + Bool readHit, writeHit; AVERT(Arena, arena); AVERT(Seg, seg); AVERT(AccessSet, mode); + shieldHit = BS_INTER(mode, SegSM(seg)); + readHit = BS_INTER(shieldHit, AccessREAD) != AccessSetEMPTY; + writeHit = BS_INTER(shieldHit, AccessWRITE) != AccessSetEMPTY; + /* If it's a read access, then the segment must be grey for a trace */ /* which is flipped. */ - AVER((mode & SegSM(seg) & AccessREAD) == 0 - || TraceSetInter(SegGrey(seg), arena->flippedTraces) != TraceSetEMPTY); + AVER(!readHit || + TraceSetInter(SegGrey(seg), arena->flippedTraces) != TraceSetEMPTY); /* If it's a write access, then the segment must have a summary that */ /* is smaller than the mutator's summary (which is assumed to be */ /* RefSetUNIV). */ - AVER((mode & SegSM(seg) & AccessWRITE) == 0 || SegSummary(seg) != RefSetUNIV); + AVER(!writeHit || SegSummary(seg) != RefSetUNIV); EVENT3(TraceAccess, arena, seg, mode); - if((mode & SegSM(seg) & AccessREAD) != 0) { /* read barrier? */ - Trace trace; - TraceId ti; + /* Write barrier deferral -- see design.mps.write-barrier.deferral. */ + if (writeHit) + seg->defer = WB_DEFER_HIT; + + if (readHit) { Rank rank; TraceSet traces; @@ -1225,7 +1285,9 @@ void TraceSegAccess(Arena arena, Seg seg, AccessSet mode) /* can go ahead and access it. */ AVER(TraceSetInter(SegGrey(seg), traces) == TraceSetEMPTY); - STATISTIC_STAT({ + STATISTIC({ + Trace trace; + TraceId ti; TRACE_SET_ITER(ti, trace, traces, arena) ++trace->readBarrierHitCount; TRACE_SET_ITER_END(ti, trace, traces, arena); @@ -1236,11 +1298,11 @@ void TraceSegAccess(Arena arena, Seg seg, AccessSet mode) /* The write barrier handling must come after the read barrier, */ /* because the latter may set the summary and raise the write barrier. */ - if((mode & SegSM(seg) & AccessWRITE) != 0) /* write barrier? */ + if (writeHit) SegSetSummary(seg, RefSetUNIV); /* The segment must now be accessible. */ - AVER((mode & SegSM(seg)) == AccessSetEMPTY); + AVER(BS_INTER(mode, SegSM(seg)) == AccessSetEMPTY); } @@ -1266,7 +1328,6 @@ mps_res_t _mps_fix2(mps_ss_t mps_ss, mps_addr_t *mps_ref_io) Tract tract; Seg seg; Res res; - Pool pool; /* Special AVER macros are used on the critical path. */ /* See */ @@ -1302,27 +1363,25 @@ mps_res_t _mps_fix2(mps_ss_t mps_ss, mps_addr_t *mps_ref_io) if (!BTGet(chunk->allocTable, i)) { /* Reference points into a chunk but not to an allocated tract. * See */ - AVER_CRITICAL(ss->rank < RankEXACT); + AVER_CRITICAL(ss->rank < RankEXACT); /* */ goto done; } tract = PageTract(&chunk->pageTable[i]); - if (TraceSetInter(TractWhite(tract), ss->traces) == TraceSetEMPTY) { - /* Reference points to a tract that is not white for any of the - * active traces. See */ - STATISTIC_STAT - ({ - if(TRACT_SEG(&seg, tract)) { - ++ss->segRefCount; - EVENT1(TraceFixSeg, seg); - } - }); + if (!TRACT_SEG(&seg, tract)) { + /* Reference points to a tract but not a segment, so it can't be white. */ goto done; } - if (!TRACT_SEG(&seg, tract)) { - /* Tracts without segments must not be condemned. */ - NOTREACHED; + /* See for where we arrange to fool + this test when walking references in the roots. */ + if (TraceSetInter(SegWhite(seg), ss->traces) == TraceSetEMPTY) { + /* Reference points to a segment that is not white for any of the + * active traces. See */ + STATISTIC({ + ++ss->segRefCount; + EVENT1(TraceFixSeg, seg); + }); goto done; } @@ -1330,11 +1389,10 @@ mps_res_t _mps_fix2(mps_ss_t mps_ss, mps_addr_t *mps_ref_io) STATISTIC(++ss->whiteSegRefCount); EVENT1(TraceFixSeg, seg); EVENT0(TraceFixWhite); - pool = TractPool(tract); - res = (*ss->fix)(pool, ss, seg, &ref); + res = (*ss->fix)(seg, ss, &ref); if (res != ResOK) { - /* PoolFixEmergency must not fail. */ - AVER_CRITICAL(ss->fix != PoolFixEmergency); + /* SegFixEmergency must not fail. */ + AVER_CRITICAL(ss->fix != SegFixEmergency); /* Fix protocol (de facto): if Fix fails, ref must be unchanged * Justification for this restriction: * A: it simplifies; @@ -1415,8 +1473,6 @@ void TraceScanSingleRef(TraceSet ts, Rank rank, Arena arena, /* Ought to be OK in emergency mode now. */ } AVER(ResOK == res); - - return; } @@ -1445,68 +1501,11 @@ Res TraceScanArea(ScanState ss, Word *base, Word *limit, it's safe to accumulate now so that we can tail-call scan_area. */ ss->scannedSize += AddrOffset(base, limit); - + return scan_area(&ss->ss_s, base, limit, closure); } -/* traceCondemnAll -- condemn everything and notify all the chains */ - -static Res traceCondemnAll(Trace trace) -{ - Res res; - Arena arena; - Ring poolNode, nextPoolNode, chainNode, nextChainNode; - Bool haveWhiteSegs = FALSE; - - arena = trace->arena; - AVERT(Arena, arena); - - /* 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); - - ChainStartGC(chain, trace); - } - return ResOK; - -failBegin: - /* .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; -} - - -/* Collection control parameters */ - -double TraceWorkFactor = 0.25; - - /* TraceStart -- condemn a set of objects and start collection * * TraceStart should be passed a trace with state TraceINIT, i.e., @@ -1556,6 +1555,7 @@ Res TraceStart(Trace trace, double mortality, double finishingTime) AVER(0.0 <= mortality); AVER(mortality <= 1.0); AVER(finishingTime >= 0.0); + AVER(trace->condemned > 0); arena = trace->arena; @@ -1584,7 +1584,7 @@ Res TraceStart(Trace trace, double mortality, double finishingTime) /* 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); + SegGreyen(seg, trace); if(TraceSetIsMember(SegGrey(seg), trace)) { trace->foundation += size; } @@ -1602,7 +1602,7 @@ Res TraceStart(Trace trace, double mortality, double finishingTime) res = RootsIterate(ArenaGlobals(arena), rootGrey, (void *)trace); AVER(res == ResOK); - STATISTIC_STAT(EVENT2(ArenaWriteFaults, arena, arena->writeBarrierHitCount)); + STATISTIC(EVENT2(ArenaWriteFaults, arena, arena->writeBarrierHitCount)); /* Calculate the rate of scanning. */ { @@ -1616,8 +1616,10 @@ Res TraceStart(Trace trace, double mortality, double finishingTime) /* integer, so try to make sure it fits. */ if(nPolls >= (double)LONG_MAX) nPolls = (double)LONG_MAX; - /* rate equals scanning work per number of polls available */ - trace->rate = (trace->foundation + sSurvivors) / (unsigned long)nPolls + 1; + /* One quantum of work equals total tracing work divided by number + * of polls, plus one to ensure it's not zero. */ + trace->quantumWork + = (trace->foundation + sSurvivors) / (unsigned long)nPolls + 1; } /* TODO: compute rate of scanning here. */ @@ -1625,12 +1627,7 @@ Res TraceStart(Trace trace, double mortality, double finishingTime) EVENT8(TraceStart, trace, mortality, finishingTime, trace->condemned, trace->notCondemned, trace->foundation, trace->white, - trace->rate); - - STATISTIC_STAT(EVENT7(TraceStatCondemn, trace, - trace->condemned, trace->notCondemned, - trace->foundation, trace->rate, - mortality, finishingTime)); + trace->quantumWork); trace->state = TraceUNFLIPPED; TracePostStartMessage(trace); @@ -1640,14 +1637,24 @@ Res TraceStart(Trace trace, double mortality, double finishingTime) } +/* traceWork -- a measure of the work done for this trace. + * + * See design.mps.type.work. + */ + +#define traceWork(trace) ((Work)((trace)->segScanSize + (trace)->rootScanSize)) + + /* TraceAdvance -- progress a trace by one step */ void TraceAdvance(Trace trace) { Arena arena; + Work oldWork, newWork; AVERT(Trace, trace); arena = trace->arena; + oldWork = traceWork(trace); switch (trace->state) { case TraceUNFLIPPED: @@ -1676,6 +1683,10 @@ void TraceAdvance(Trace trace) NOTREACHED; break; } + + newWork = traceWork(trace); + AVER(newWork >= oldWork); + arena->tracedWork += newWork - oldWork; } @@ -1689,23 +1700,39 @@ Res TraceStartCollectAll(Trace *traceReturn, Arena arena, int why) { Trace trace = NULL; Res res; - double finishingTime; + double mortality, finishingTime; + Ring chainNode, chainNext; AVERT(Arena, arena); AVER(arena->busyTraces == TraceSetEMPTY); res = TraceCreate(&trace, arena, why); AVER(res == ResOK); /* succeeds because no other trace is busy */ - res = traceCondemnAll(trace); + + TraceCondemnStart(trace); + + /* Condemn all generations in all chains, plus the top generation. */ + RING_FOR(chainNode, &arena->chainRing, chainNext) { + size_t i; + Chain chain = RING_ELT(Chain, chainRing, chainNode); + AVERT(Chain, chain); + for (i = 0; i < chain->genCount; ++i) { + GenDesc gen = &chain->gens[i]; + AVERT(GenDesc, gen); + GenDescStartTrace(gen, trace); + } + } + GenDescStartTrace(&arena->topGen, trace); + + res = TraceCondemnEnd(&mortality, trace); if(res != ResOK) /* should try some other trace, really @@@@ */ goto failCondemn; - finishingTime = ArenaAvail(arena) - - trace->condemned * (1.0 - arena->topGen.mortality); + finishingTime = ArenaAvail(arena) - trace->condemned * (1.0 - mortality); if(finishingTime < 0) { /* Run out of time, should really try a smaller collection. @@@@ */ finishingTime = 0.0; } - res = TraceStart(trace, arena->topGen.mortality, finishingTime); + res = TraceStart(trace, mortality, finishingTime); if (res != ResOK) goto failStart; *traceReturn = trace; @@ -1720,32 +1747,29 @@ Res TraceStartCollectAll(Trace *traceReturn, Arena arena, int why) if the assertion isn't hit, so drop through anyway. */ NOTREACHED; failCondemn: - TraceDestroy(trace); - /* We don't know how long it'll be before another collection. Make sure - the next one starts in normal mode. */ - ArenaSetEmergency(arena, FALSE); + TraceDestroyInit(trace); return res; } -/* 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. + * trace (if any) by one quantum. + * + * The collectWorldReturn and collectWorldAllowed arguments are as for + * PolicyStartTrace. + * + * If there may be more work to do, update *workReturn with a measure + * of the work done and return TRUE. Otherwise return FALSE. */ -Size TracePoll(Globals globals) +Bool TracePoll(Work *workReturn, Bool *collectWorldReturn, Globals globals, + Bool collectWorldAllowed) { Trace trace; Arena arena; - Size oldScannedSize, scannedSize, pollEnd; + Work oldWork, newWork, work, endWork; AVERT(Globals, globals); arena = GlobalsArena(globals); @@ -1754,25 +1778,24 @@ Size TracePoll(Globals globals) trace = ArenaTrace(arena, (TraceId)0); } else { /* No traces are running: consider starting one now. */ - if (!PolicyStartTrace(&trace, arena)) - return (Size)0; + if (!PolicyStartTrace(&trace, collectWorldReturn, arena, + collectWorldAllowed)) + return FALSE; } AVER(arena->busyTraces == TraceSetSingle(trace)); - oldScannedSize = traceWorkClock(trace); - pollEnd = oldScannedSize + trace->rate; + oldWork = traceWork(trace); + endWork = oldWork + trace->quantumWork; 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; + } while (trace->state != TraceFINISHED && traceWork(trace) < endWork); + newWork = traceWork(trace); + AVER(newWork >= oldWork); + work = newWork - oldWork; + if (trace->state == TraceFINISHED) + TraceDestroyFinished(trace); + *workReturn = work; + return TRUE; } @@ -1780,6 +1803,7 @@ Size TracePoll(Globals globals) Res TraceDescribe(Trace trace, mps_lib_FILE *stream, Count depth) { + Ring node, next; Res res; const char *state; @@ -1806,17 +1830,30 @@ Res TraceDescribe(Trace trace, mps_lib_FILE *stream, Count depth) " 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, + " quantumWork $U\n", (WriteFU)trace->quantumWork, " rootScanSize $U\n", (WriteFU)trace->rootScanSize, - " rootCopiedSize $U\n", (WriteFU)trace->rootCopiedSize, + STATISTIC_WRITE(" rootCopiedSize $U\n", + (WriteFU)trace->rootCopiedSize) " segScanSize $U\n", (WriteFU)trace->segScanSize, - " segCopiedSize $U\n", (WriteFU)trace->segCopiedSize, + STATISTIC_WRITE(" segCopiedSize $U\n", + (WriteFU)trace->segCopiedSize) " forwardedSize $U\n", (WriteFU)trace->forwardedSize, " preservedInPlaceSize $U\n", (WriteFU)trace->preservedInPlaceSize, + NULL); + if (res != ResOK) + return res; + + RING_FOR(node, &trace->genRing, next) { + GenDesc gen = GenDescOfTraceRing(node, trace); + res = GenDescDescribe(gen, stream, depth + 2); + if (res != ResOK) + return res; + } + + res = WriteF(stream, depth, "} Trace $P\n", (WriteFP)trace, NULL); return res; @@ -1825,7 +1862,7 @@ Res TraceDescribe(Trace trace, mps_lib_FILE *stream, Count depth) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2016 Ravenbrook Limited + * Copyright (C) 2001-2018 Ravenbrook Limited * . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. diff --git a/mps/code/traceanc.c b/mps/code/traceanc.c index c57500c0b65..1e3249578ef 100644 --- a/mps/code/traceanc.c +++ b/mps/code/traceanc.c @@ -1,7 +1,7 @@ /* traceanc.c: ANCILLARY SUPPORT FOR TRACER * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. + * Copyright (c) 2001-2018 Ravenbrook Limited. * See end of file for license. * Portions copyright (C) 2002 Global Graphics Software. * @@ -45,7 +45,7 @@ * See . */ -#define TraceStartMessageSig ((Sig)0x51926535) /* SIG TRaceStartMeSsage */ +#define TraceStartMessageSig ((Sig)0x51926535) /* SIGnature TRaceStartMeSsage */ /* .whybuf: * .whybuf.len: Length (in chars) of a char buffer used to store the @@ -274,7 +274,7 @@ void TracePostStartMessage(Trace trace) /* TraceMessage -- type of trace end messages */ -#define TraceMessageSig ((Sig)0x51926359) +#define TraceMessageSig ((Sig)0x51926359) /* SIGnature TRace MeSsaGe */ typedef struct TraceMessageStruct { Sig sig; @@ -437,11 +437,9 @@ void TracePostMessage(Trace trace) Bool TraceIdMessagesCheck(Arena arena, TraceId ti) { - CHECKL(!arena->tsMessage[ti] - || TraceStartMessageCheck(arena->tsMessage[ti])); - CHECKL(!arena->tMessage[ti] - || TraceMessageCheck(arena->tMessage[ti])); - CHECKL(! (arena->tsMessage[ti] && !arena->tMessage[ti]) ); + CHECKL(!arena->tsMessage[ti] || TraceStartMessageCheck(arena->tsMessage[ti])); + CHECKL(!arena->tsMessage[ti] || arena->tMessage[ti]); + CHECKL(!arena->tMessage[ti] || TraceMessageCheck(arena->tMessage[ti])); return TRUE; } @@ -467,12 +465,12 @@ Res TraceIdMessagesCreate(Arena arena, TraceId ti) AVER(!arena->tsMessage[ti]); AVER(!arena->tMessage[ti]); - res = ControlAlloc(&p, arena, sizeof(TraceStartMessageStruct), FALSE); + res = ControlAlloc(&p, arena, sizeof(TraceStartMessageStruct)); if(res != ResOK) goto failTraceStartMessage; tsMessage = p; - res = ControlAlloc(&p, arena, sizeof(TraceMessageStruct), FALSE); + res = ControlAlloc(&p, arena, sizeof(TraceMessageStruct)); if(res != ResOK) goto failTraceMessage; tMessage = p; @@ -530,12 +528,11 @@ void TraceIdMessagesDestroy(Arena arena, TraceId ti) -/* -------- ArenaRelease, ArenaClamp, ArenaPark -------- */ +/* ----- ArenaRelease, ArenaClamp, ArenaPark, ArenaPostmortem ----- */ -/* ArenaRelease, ArenaClamp, ArenaPark -- allow/prevent collection work. - * - * These functions allow or prevent collection work. +/* ArenaRelease, ArenaClamp, ArenaPark, ArenaPostmortem -- + * allow/prevent collection work. */ @@ -572,27 +569,86 @@ void ArenaPark(Globals globals) TraceId ti; Trace trace; Arena arena; + Clock start; AVERT(Globals, globals); arena = GlobalsArena(globals); globals->clamped = TRUE; + start = ClockNow(); while(arena->busyTraces != TraceSetEMPTY) { /* Advance all active traces. */ TRACE_SET_ITER(ti, trace, arena->busyTraces, arena) TraceAdvance(trace); if(trace->state == TraceFINISHED) { - TraceDestroy(trace); + TraceDestroyFinished(trace); } TRACE_SET_ITER_END(ti, trace, arena->busyTraces, arena); } - - /* Clear any emergency flag so that the next collection starts normally. - Any traces that have been finished may have reclaimed memory. */ - ArenaSetEmergency(arena, FALSE); + + ArenaAccumulateTime(arena, start, ClockNow()); + + /* All traces have finished so there must not be an emergency. */ + AVER(!ArenaEmergency(arena)); } + +/* arenaExpose -- discard all protection from MPS-managed memory + * + * This is called by ArenaPostmortem, which we expect only to be used + * after a fatal error. So we use the lowest-level description of the + * MPS-managed memory (the chunk ring page tables) to avoid the risk + * of higher-level structures (like the segments) having been + * corrupted. + * + * After calling this function memory may not be in a consistent + * state, so it is not safe to continue running the MPS. If you need + * to expose memory but continue running the MPS, use + * ArenaExposeRemember instead. + */ + +static void arenaExpose(Arena arena) +{ + Ring node, next; + RING_FOR(node, &arena->chunkRing, next) { + Chunk chunk = RING_ELT(Chunk, arenaRing, node); + Index i; + for (i = 0; i < chunk->pages; ++i) { + if (Method(Arena, arena, chunkPageMapped)(chunk, i)) { + ProtSet(PageIndexBase(chunk, i), PageIndexBase(chunk, i + 1), + AccessSetEMPTY); + } + } + } +} + + +/* ArenaPostmortem -- enter the postmortem state */ + +void ArenaPostmortem(Globals globals) +{ + Arena arena = GlobalsArena(globals); + + /* Ensure lock is released. */ + while (LockIsHeld(globals->lock)) { + LockReleaseRecursive(globals->lock); + } + + /* Remove the arena from the global arena ring so that it no longer + * handles protection faults. (Don't call arenaDenounce because that + * needs to claim the global ring lock, but that might already be + * held, for example if we are inside ArenaAccess.) */ + RingRemove(&globals->globalRing); + + /* Clamp the arena so that ArenaPoll does nothing. */ + ArenaClamp(globals); + + /* Remove all protection from mapped pages. */ + arenaExpose(arena); +} + + /* ArenaStartCollect -- start a collection of everything in the * arena; leave unclamped. */ @@ -674,7 +730,7 @@ static Res arenaRememberSummaryOne(Globals global, Addr base, RefSet summary) RememberedSummaryBlock newBlock; int res; - res = ControlAlloc(&p, arena, sizeof *newBlock, FALSE); + res = ControlAlloc(&p, arena, sizeof *newBlock); if(res != ResOK) { return res; } @@ -720,7 +776,7 @@ void ArenaExposeRemember(Globals globals, Bool remember) do { base = SegBase(seg); - if(IsSubclassPoly(ClassOfSeg(seg), GCSegClassGet())) { + if (IsA(GCSeg, seg)) { if(remember) { RefSet summary; @@ -763,7 +819,7 @@ void ArenaRestoreProtection(Globals globals) } b = SegOfAddr(&seg, arena, block->the[i].base); if(b && SegBase(seg) == block->the[i].base) { - AVER(IsSubclassPoly(ClassOfSeg(seg), GCSegClassGet())); + AVER(IsA(GCSeg, seg)); SegSetSummary(seg, block->the[i].summary); } else { /* Either seg has gone or moved, both of which are */ @@ -796,7 +852,7 @@ static void arenaForgetProtection(Globals globals) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited + * Copyright (C) 2001-2018 Ravenbrook Limited * . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. diff --git a/mps/code/tract.c b/mps/code/tract.c index 9aa815f47ba..f2143b7783b 100644 --- a/mps/code/tract.c +++ b/mps/code/tract.c @@ -1,7 +1,7 @@ /* tract.c: PAGE TABLES * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 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. @@ -47,10 +47,7 @@ Bool TractCheck(Tract tract) CHECKL(AddrIsArenaGrain(TractBase(tract), TractArena(tract))); } if (TractHasSeg(tract)) { - CHECKL(TraceSetCheck(TractWhite(tract))); - CHECKU(Seg, (Seg)TractP(tract)); - } else { - CHECKL(TractWhite(tract) == TraceSetEMPTY); + CHECKU(Seg, TractSeg(tract)); } return TRUE; } @@ -60,14 +57,12 @@ Bool TractCheck(Tract tract) void TractInit(Tract tract, Pool pool, Addr base) { - AVER(tract != NULL); - AVERT(Pool, pool); + AVER_CRITICAL(tract != NULL); + AVERT_CRITICAL(Pool, pool); tract->pool.pool = pool; tract->base = base; - tract->p = NULL; - tract->white = TraceSetEMPTY; - tract->hasSeg = FALSE; + tract->seg = NULL; AVERT(Tract, tract); @@ -174,6 +169,7 @@ Res ChunkInit(Chunk chunk, Arena arena, Addr base, Addr limit, Size reserved, Count pages; Shift pageShift; Size pageTableSize; + Addr allocBase; void *p; Res res; @@ -196,6 +192,7 @@ Res ChunkInit(Chunk chunk, Arena arena, Addr base, Addr limit, Size reserved, chunk->reserved = reserved; size = ChunkSize(chunk); + /* .overhead.pages: Chunk overhead for the page allocation table. */ chunk->pages = pages = size >> pageShift; res = BootAlloc(&p, boot, (size_t)BTSize(pages), MPS_PF_ALIGN); if (res != ResOK) @@ -205,7 +202,7 @@ Res ChunkInit(Chunk chunk, Arena arena, Addr base, Addr limit, Size reserved, pageTableSize = SizeAlignUp(pages * sizeof(PageUnion), chunk->pageSize); chunk->pageTablePages = pageTableSize >> pageShift; - res = (arena->class->chunkInit)(chunk, boot); + res = Method(Arena, arena, chunkInit)(chunk, boot); if (res != ResOK) goto failClassInit; @@ -219,12 +216,14 @@ Res ChunkInit(Chunk chunk, Arena arena, Addr base, Addr limit, Size reserved, /* Init allocTable after class init, because it might be mapped there. */ BTResRange(chunk->allocTable, 0, pages); + /* Check that there is some usable address space remaining in the chunk. */ + allocBase = PageIndexBase(chunk, chunk->allocBase); + AVER(allocBase < chunk->limit); + /* 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); + res = ArenaFreeLandInsert(arena, allocBase, chunk->limit); if (res != ResOK) goto failLandInsert; } @@ -239,7 +238,7 @@ Res ChunkInit(Chunk chunk, Arena arena, Addr base, Addr limit, Size reserved, return ResOK; failLandInsert: - (arena->class->chunkFinish)(chunk); + Method(Arena, arena, chunkFinish)(chunk); /* .no-clean: No clean-ups needed past this point for boot, as we will discard the chunk. */ failClassInit: @@ -273,7 +272,7 @@ void ChunkFinish(Chunk chunk) /* Finish all other fields before class finish, because they might be */ /* unmapped there. */ - (*arena->class->chunkFinish)(chunk); + Method(Arena, arena, chunkFinish)(chunk); } @@ -452,11 +451,11 @@ void PageAlloc(Chunk chunk, Index pi, Pool pool) Addr base; Page page; - AVERT(Chunk, chunk); - AVER(pi >= chunk->allocBase); - AVER(pi < chunk->pages); - AVER(!BTGet(chunk->allocTable, pi)); - AVERT(Pool, pool); + AVERT_CRITICAL(Chunk, chunk); + AVER_CRITICAL(pi >= chunk->allocBase); + AVER_CRITICAL(pi < chunk->pages); + AVER_CRITICAL(!BTGet(chunk->allocTable, pi)); + AVERT_CRITICAL(Pool, pool); page = ChunkPage(chunk, pi); tract = PageTract(page); @@ -472,9 +471,9 @@ void PageInit(Chunk chunk, Index pi) { Page page; - AVERT(Chunk, chunk); - AVER(pi < chunk->pages); - + AVERT_CRITICAL(Chunk, chunk); + AVER_CRITICAL(pi < chunk->pages); + page = ChunkPage(chunk, pi); BTRes(chunk->allocTable, pi); @@ -494,13 +493,12 @@ void PageFree(Chunk chunk, Index pi) AVER(BTGet(chunk->allocTable, pi)); PageInit(chunk, pi); - return; } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/tract.h b/mps/code/tract.h index ce7c602a176..3ffabd818c9 100644 --- a/mps/code/tract.h +++ b/mps/code/tract.h @@ -1,7 +1,7 @@ /* tract.h: PAGE TABLE INTERFACE * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. */ @@ -42,10 +42,8 @@ typedef union PagePoolUnion { typedef struct TractStruct { /* Tract structure */ PagePoolUnion pool; /* MUST BE FIRST ( pool) */ - void *p; /* pointer for use of owning pool */ + Seg seg; /* NULL or segment containing tract */ Addr base; /* Base address of the tract */ - TraceSet white : TraceLIMIT; /* traces for which tract is white */ - BOOLFIELD(hasSeg); /* does tract have a seg in p? */ } TractStruct; @@ -56,12 +54,8 @@ 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))) +#define TractHasSeg(tract) ((tract)->seg != NULL) +#define TractSeg(tract) ((tract)->seg) extern Bool TractCheck(Tract tract); extern void TractInit(Tract tract, Pool pool, Addr base); @@ -74,13 +68,13 @@ extern void TractFinish(Tract tract); */ #define TRACT_SEG(segReturn, tract) \ - (TractHasSeg(tract) && ((*(segReturn) = (Seg)TractP(tract)), TRUE)) + (TractHasSeg(tract) && ((*(segReturn) = (tract)->seg), TRUE)) -#define TRACT_SET_SEG(tract, seg) \ - (TractSetHasSeg(tract, TRUE), TractSetP(tract, seg)) +#define TRACT_SET_SEG(tract, _seg) \ + BEGIN (tract)->seg = (_seg); END #define TRACT_UNSET_SEG(tract) \ - (TractSetHasSeg(tract, FALSE), TractSetP(tract, NULL)) + BEGIN (tract)->seg = NULL; END /* PageUnion -- page descriptor @@ -110,7 +104,7 @@ typedef union PageUnion { /* page structure */ #define PagePool(page) RVALUE((page)->pool.pool) #define PageIsAllocated(page) RVALUE(PagePool(page) != NULL) #define PageState(page) RVALUE((page)->pool.state) -#define PageSpareRing(page) RVALUE(&(page)->spare.spareRing) +#define PageSpareRing(page) (&(page)->spare.spareRing) #define PageOfSpareRing(node) PARENT(PageUnion, spare, RING_ELT(PageSpare, spareRing, node)) #define PageSetPool(page, _pool) \ @@ -259,7 +253,7 @@ extern void PageFree(Chunk chunk, Index pi); /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/tree.c b/mps/code/tree.c index f87e8364ea7..ff5861076ad 100644 --- a/mps/code/tree.c +++ b/mps/code/tree.c @@ -1,7 +1,7 @@ /* tree.c: BINARY TREE IMPLEMENTATION * * $Id$ - * Copyright (C) 2014-2015 Ravenbrook Limited. See end of file for license. + * Copyright (C) 2014-2018 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). @@ -230,20 +230,20 @@ Bool TreeInsert(Tree *treeReturn, Tree root, Tree node, */ Bool TreeTraverseMorris(Tree tree, TreeVisitor visit, - void *closureP, Size closureS) + void *closure) { Tree node; Bool visiting = TRUE; AVERT(Tree, tree); AVER(FUNCHECK(visit)); - /* closureP, closureS arbitrary */ + /* closure arbitrary */ node = tree; while (node != TreeEMPTY) { if (node->left == TreeEMPTY) { if (visiting) - visiting = visit(node, closureP, closureS); + visiting = visit(node, closure); node = node->right; } else { Tree pre = node->left; @@ -256,7 +256,7 @@ Bool TreeTraverseMorris(Tree tree, TreeVisitor visit, if (pre->right == node) { pre->right = TreeEMPTY; if (visiting) - visiting = visit(node, closureP, closureS); + visiting = visit(node, closure); else if (node == tree) return FALSE; node = node->right; @@ -323,13 +323,13 @@ static Tree stepUpLeft(Tree node, Tree *parentIO) Bool TreeTraverse(Tree tree, TreeCompareFunction compare, TreeKeyFunction key, - TreeVisitor visit, void *closureP, Size closureS) + TreeVisitor visit, void *closure) { Tree parent, node; AVERT(Tree, tree); AVER(FUNCHECK(visit)); - /* closureP, closureS arbitrary */ + /* closure arbitrary */ parent = TreeEMPTY; node = tree; @@ -343,7 +343,7 @@ Bool TreeTraverse(Tree tree, AVER(compare(parent, key(node)) == CompareLESS); goto down; } - if (!visit(node, closureP, closureS)) + if (!visit(node, closure)) goto abort; if (TreeHasRight(node)) { node = stepDownRight(node, &parent); @@ -359,7 +359,7 @@ Bool TreeTraverse(Tree tree, goto up; } node = stepUpRight(node, &parent); - if (!visit(node, closureP, closureS)) + if (!visit(node, closure)) goto abort; if (!TreeHasRight(node)) goto up; @@ -408,7 +408,8 @@ void TreeRotateLeft(Tree *treeIO) * ordering. */ -void TreeRotateRight(Tree *treeIO) { +void TreeRotateRight(Tree *treeIO) +{ Tree tree, left; AVER(treeIO != NULL); @@ -539,14 +540,14 @@ void TreeBalance(Tree *treeIO) * See . */ void TreeTraverseAndDelete(Tree *treeIO, TreeVisitor visitor, - void *closureP, Size closureS) + void *closure) { Tree *treeref = treeIO; AVER(treeIO != NULL); AVERT(Tree, *treeIO); AVER(FUNCHECK(visitor)); - /* closureP and closureS are arbitrary */ + /* closure arbitrary */ TreeToVine(treeIO); @@ -554,7 +555,7 @@ void TreeTraverseAndDelete(Tree *treeIO, TreeVisitor visitor, 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)) { + if ((*visitor)(tree, closure)) { /* Delete current node. */ *treeref = next; } else { @@ -568,7 +569,7 @@ void TreeTraverseAndDelete(Tree *treeIO, TreeVisitor visitor, /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2014-2015 Ravenbrook Limited . + * Copyright (C) 2014-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/tree.h b/mps/code/tree.h index 7b463067f0e..a4f883c48cf 100644 --- a/mps/code/tree.h +++ b/mps/code/tree.h @@ -123,13 +123,13 @@ extern Bool TreeFindNext(Tree *treeReturn, Tree root, extern Bool TreeInsert(Tree *treeReturn, Tree root, Tree node, TreeKey key, TreeCompareFunction compare); -typedef Bool TreeVisitor(Tree tree, void *closureP, Size closureS); +typedef Bool TreeVisitor(Tree tree, void *closure); extern Bool TreeTraverse(Tree tree, TreeCompareFunction compare, TreeKeyFunction key, - TreeVisitor visit, void *closureP, Size closureS); + TreeVisitor visit, void *closure); extern Bool TreeTraverseMorris(Tree tree, TreeVisitor visit, - void *closureP, Size closureS); + void *closure); extern void TreeRotateLeft(Tree *nodeIO); extern void TreeRotateRight(Tree *nodeIO); @@ -139,7 +139,7 @@ extern Count TreeToVine(Tree *treeIO); extern void TreeBalance(Tree *treeIO); extern void TreeTraverseAndDelete(Tree *treeIO, TreeVisitor visitor, - void *closureP, Size closureS); + void *closure); #endif /* tree_h */ diff --git a/mps/code/version.c b/mps/code/version.c index 9a212dbbb77..7b3cbad083c 100644 --- a/mps/code/version.c +++ b/mps/code/version.c @@ -1,7 +1,7 @@ /* version.c: VERSION INSPECTION * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. + * Copyright (c) 2001-2018 Ravenbrook Limited. * See end of file for license. * * PURPOSE @@ -13,7 +13,8 @@ * * .design: See , but -- to let you in on a * secret -- it works by declaring a string with all the necessary info - * in. */ + * in. + */ #include "mpm.h" @@ -38,7 +39,7 @@ SRCID(version, "$Id$"); * .release.old: before 2006-02-01 the style was "release.epcore.chub". */ -#define MPS_RELEASE "release/1.115.0" +#define MPS_RELEASE "release/1.117.0" /* MPSCopyrightNotice -- copyright notice for the binary @@ -49,15 +50,15 @@ SRCID(version, "$Id$"); extern char MPSCopyrightNotice[]; char MPSCopyrightNotice[] = - "Portions copyright (c) 2010-2014 Ravenbrook Limited and Global Graphics Software."; + "Portions copyright (c) 2010-2018 Ravenbrook Limited and Global Graphics Software."; /* MPSVersion -- return version string * * The value of MPSVersion is a declared object comprising the - * concatenation of all the version info. The "@(#)" prefix - * is the convention used by the BSD Unix command what(1); - * see also guide.mps.version. + * concatenation of all the version info. The "@(#)" prefix is the + * convention used by the BSD Unix command what(1); see also + * design.mps.version.impl.tool. */ extern char MPSVersionString[]; @@ -74,7 +75,7 @@ char *MPSVersion(void) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited + * Copyright (C) 2001-2018 Ravenbrook Limited * . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. diff --git a/mps/code/vmix.c b/mps/code/vmix.c index 61e093a02e8..694a1625028 100644 --- a/mps/code/vmix.c +++ b/mps/code/vmix.c @@ -1,7 +1,7 @@ -/* vmix.c: VIRTUAL MEMORY MAPPING FOR UNIX (ISH) +/* vmix.c: VIRTUAL MEMORY MAPPING (POSIX) * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 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 @@ -10,7 +10,7 @@ * copied from vli.c (Linux) which was itself copied from vmo1.c (OSF/1 * / DIGITAL UNIX / Tru64). * - * .deployed: Currently used on Darwin (OS X) and FreeBSD. + * .deployed: Currently used on Darwin (macOS) and FreeBSD. * * .design: See . .design.mmap: mmap(2) is used to * reserve address space by creating a mapping with page access none. @@ -39,22 +39,17 @@ */ #include "mpm.h" + +#if !defined(MPS_OS_FR) && !defined(MPS_OS_LI) && !defined(MPS_OS_XC) +#error "vmix.c is specific to MPS_OS_FR, MPS_OS_LI or MPS_OS_XC" +#endif + #include "vm.h" -/* for mmap(2), munmap(2) */ -#include +#include /* errno */ #include /* see .feature.li in config.h */ - -/* for errno(2) */ -#include - -/* for getpagesize(3) */ -#include - - -#if !defined(MPS_OS_FR) && !defined(MPS_OS_XC) && !defined(MPS_OS_LI) -#error "vmix.c is Unix-like specific, currently MPS_OS_FR XC LI" -#endif +#include /* mmap, munmap */ +#include /* getpagesize */ SRCID(vmix, "$Id$"); @@ -225,7 +220,7 @@ void VMUnmap(VM vm, Addr base, Addr limit) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/vmw3.c b/mps/code/vmw3.c index a6cfc7df6c8..e779eaec943 100644 --- a/mps/code/vmw3.c +++ b/mps/code/vmw3.c @@ -1,7 +1,7 @@ /* vmw3.c: VIRTUAL MEMORY MAPPING FOR WIN32 * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. * * .design: See . * @@ -39,13 +39,13 @@ */ #include "mpm.h" -#include "vm.h" -#ifndef MPS_OS_W3 -#error "vmw3.c is Win32 specific, but MPS_OS_W3 is not set" +#if !defined(MPS_OS_W3) +#error "vmw3.c is specific to MPS_OS_W3" #endif #include "mpswin.h" +#include "vm.h" SRCID(vmw3, "$Id$"); @@ -229,7 +229,7 @@ void VMUnmap(VM vm, Addr base, Addr limit) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2018 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/w3i3mv.nmk b/mps/code/w3i3mv.nmk index eb8bacce7cb..29af20974ad 100644 --- a/mps/code/w3i3mv.nmk +++ b/mps/code/w3i3mv.nmk @@ -1,20 +1,19 @@ # w3i3mv.nmk: WINDOWS (IA-32) NMAKE FILE -*- makefile -*- # # $Id$ -# Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. PFM = w3i3mv MPMPF = \ [lockw3] \ [mpsiw3] \ - [prmci3w3] \ - [proti3] \ + [prmci3] \ + [prmcw3] \ + [prmcw3i3] \ [protw3] \ [spw3i3] \ - [ssw3i3mv] \ [thw3] \ - [thw3i3] \ [vmw3] !INCLUDE commpre.nmk @@ -24,7 +23,7 @@ MPMPF = \ # C. COPYRIGHT AND LICENSE # -# Copyright (C) 2001-2014 Ravenbrook Limited . +# Copyright (C) 2001-2016 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. # diff --git a/mps/code/w3i3pc.nmk b/mps/code/w3i3pc.nmk index 82be17e4057..df31bd3543e 100644 --- a/mps/code/w3i3pc.nmk +++ b/mps/code/w3i3pc.nmk @@ -1,20 +1,19 @@ # w3i3pc.nmk: WINDOWS (IA-32) NMAKE FILE -*- makefile -*- # # $Id$ -# Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. PFM = w3i3pc MPMPF = \ [lockw3] \ [mpsiw3] \ - [prmci3w3] \ - [proti3] \ + [prmci3] \ + [prmcw3] \ + [prmcw3i3] \ [protw3] \ [spw3i3] \ - [ssw3i3pc] \ [thw3] \ - [thw3i3] \ [vmw3] !INCLUDE commpre.nmk @@ -24,7 +23,7 @@ MPMPF = \ # C. COPYRIGHT AND LICENSE # -# Copyright (C) 2001-2014 Ravenbrook Limited . +# Copyright (C) 2001-2016 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. # diff --git a/mps/code/w3i6mv.nmk b/mps/code/w3i6mv.nmk index 2353d4cddab..2f359d610ad 100644 --- a/mps/code/w3i6mv.nmk +++ b/mps/code/w3i6mv.nmk @@ -1,20 +1,19 @@ # w3i6mv.nmk: WINDOWS (x86-64) NMAKE FILE -*- makefile -*- # # $Id$ -# Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. PFM = w3i6mv MPMPF = \ [lockw3] \ [mpsiw3] \ - [prmci6w3] \ - [proti6] \ + [prmci6] \ + [prmcw3] \ + [prmcw3i6] \ [protw3] \ [spw3i6] \ - [ssw3i6mv] \ [thw3] \ - [thw3i6] \ [vmw3] !INCLUDE commpre.nmk @@ -24,7 +23,7 @@ MPMPF = \ # C. COPYRIGHT AND LICENSE # -# Copyright (C) 2001-2014 Ravenbrook Limited . +# Copyright (C) 2001-2016 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. # diff --git a/mps/code/w3i6pc.nmk b/mps/code/w3i6pc.nmk index 272b96e5a2c..5b962e553ea 100644 --- a/mps/code/w3i6pc.nmk +++ b/mps/code/w3i6pc.nmk @@ -2,8 +2,8 @@ # # 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. +# $Id$ +# Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. PFM = w3i6pc @@ -12,13 +12,12 @@ CFLAGSTARGETPRE = /Tamd64-coff MPMPF = \ [lockw3] \ [mpsiw3] \ - [prmci6w3] \ - [proti6] \ + [prmci6] \ + [prmcw3] \ + [prmcw3i6] \ [protw3] \ [spw3i6] \ - [ssw3i6pc] \ [thw3] \ - [thw3i6] \ [vmw3] !INCLUDE commpre.nmk @@ -28,7 +27,7 @@ MPMPF = \ # C. COPYRIGHT AND LICENSE # -# Copyright (C) 2001-2014 Ravenbrook Limited . +# Copyright (C) 2001-2016 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. # diff --git a/mps/code/w3mv/.renamed-gitignore b/mps/code/w3mv/.renamed-gitignore new file mode 120000 index 00000000000..c5c99a6a89c --- /dev/null +++ b/mps/code/w3mv/.renamed-gitignore @@ -0,0 +1 @@ +.p4ignore \ No newline at end of file diff --git a/mps/code/walk.c b/mps/code/walk.c index 38278cce1ad..2015e999855 100644 --- a/mps/code/walk.c +++ b/mps/code/walk.c @@ -1,7 +1,7 @@ /* walk.c: OBJECT WALKER * * $Id$ - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. */ #include "mpm.h" @@ -14,7 +14,7 @@ SRCID(walk, "$Id$"); */ -#define FormattedObjectsStepClosureSig ((Sig)0x519F05C1) +#define FormattedObjectsStepClosureSig ((Sig)0x519F05C1) /* SIGnature Formatted Objects Step CLosure */ typedef struct FormattedObjectsStepClosureStruct *FormattedObjectsStepClosure; @@ -45,7 +45,7 @@ static void ArenaFormattedObjectsStep(Addr object, Format format, Pool pool, AVERT(Pool, pool); c = p; AVERT(FormattedObjectsStepClosure, c); - AVER(s == 0); + AVER(s == UNUSED_SIZE); (*c->f)((mps_addr_t)object, (mps_fmt_t)format, (mps_pool_t)pool, c->p, c->s); @@ -61,26 +61,22 @@ static void ArenaFormattedObjectsWalk(Arena arena, FormattedObjectsVisitor f, { Seg seg; FormattedObjectsStepClosure c; + Format format; 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); + /* Know that s is UNUSED_SIZE */ + AVER(s == UNUSED_SIZE); if (SegFirst(&seg, arena)) { do { - Pool pool; - pool = SegPool(seg); - if (PoolHasAttr(pool, AttrFMT)) { + if (PoolFormat(&format, SegPool(seg))) { ShieldExpose(arena, seg); - PoolWalk(pool, seg, f, p, s); + SegWalk(seg, format, f, p, s); ShieldCover(arena, seg); } } while(SegNext(&seg, arena, seg)); @@ -107,7 +103,7 @@ void mps_arena_formatted_objects_walk(mps_arena_t mps_arena, c.f = f; c.p = p; c.s = s; - ArenaFormattedObjectsWalk(arena, ArenaFormattedObjectsStep, &c, 0); + ArenaFormattedObjectsWalk(arena, ArenaFormattedObjectsStep, &c, UNUSED_SIZE); ArenaLeave(arena); } @@ -145,8 +141,7 @@ void mps_arena_formatted_objects_walk(mps_arena_t mps_arena, * * Defined as a subclass of ScanState. */ -/* SIGnature Roots Step CLOsure */ -#define rootsStepClosureSig ((Sig)0x51965C10) +#define rootsStepClosureSig ((Sig)0x51965C10) /* SIGnature Roots Step CLOsure */ typedef struct rootsStepClosureStruct *rootsStepClosure; typedef struct rootsStepClosureStruct { @@ -185,7 +180,7 @@ static Bool rootsStepClosureCheck(rootsStepClosure rsc) static void rootsStepClosureInit(rootsStepClosure rsc, Globals arena, Trace trace, - PoolFixMethod rootFix, + SegFixMethod rootFix, mps_roots_stepper_t f, void *p, size_t s) { ScanState ss; @@ -229,13 +224,12 @@ static void rootsStepClosureFinish(rootsStepClosure rsc) * This doesn't cause further scanning of transitive references, it just * calls the client closure. */ -static Res RootsWalkFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) +static Res RootsWalkFix(Seg seg, ScanState ss, Ref *refIO) { rootsStepClosure rsc; Ref ref; - - UNUSED(pool); + AVERT(Seg, seg); AVERT(ScanState, ss); AVER(refIO != NULL); rsc = ScanState2rootsStepClosure(ss); @@ -243,10 +237,6 @@ static Res RootsWalkFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) 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(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); @@ -319,14 +309,19 @@ static Res ArenaRootsWalk(Globals arenaGlobals, mps_roots_stepper_t f, if (res != ResOK) return res; - /* ArenaRootsWalk only passes references to GCable pools to the client. */ - /* NOTE: I'm not sure why this is. RB 2012-07-24 */ + /* .roots-walk.first-stage: In order to fool MPS_FIX12 into calling + _mps_fix2 for a reference in a root, the reference must pass the + first-stage test (against the summary of the trace's white + set), so make the summary universal. */ + trace->white = ZoneSetUNIV; + + /* .roots-walk.second-stage: In order to fool _mps_fix2 into calling + our fix function (RootsWalkFix), the reference must be to a + segment that is white for the trace, so make all segments white + for the trace. */ if (SegFirst(&seg, arena)) { do { - if (PoolHasAttr(SegPool(seg), AttrGC)) { - res = TraceAddWhite(trace, seg); - AVER(res == ResOK); - } + SegSetWhite(seg, TraceSetAdd(SegWhite(seg), trace)); } while (SegNext(&seg, arena, seg)); } @@ -349,17 +344,14 @@ static Res ArenaRootsWalk(Globals arenaGlobals, mps_roots_stepper_t f, /* Turn segments black again. */ if (SegFirst(&seg, arena)) { do { - if (PoolHasAttr(SegPool(seg), AttrGC)) { - SegSetGrey(seg, TraceSetDel(SegGrey(seg), trace)); - SegSetWhite(seg, TraceSetDel(SegWhite(seg), trace)); - } + SegSetWhite(seg, TraceSetDel(SegWhite(seg), trace)); } while (SegNext(&seg, arena, seg)); } rootsStepClosureFinish(rsc); /* Make this trace look like any other finished trace. */ trace->state = TraceFINISHED; - TraceDestroy(trace); + TraceDestroyFinished(trace); AVER(!ArenaEmergency(arena)); /* There was no allocation. */ return res; @@ -375,21 +367,23 @@ void mps_arena_roots_walk(mps_arena_t mps_arena, mps_roots_stepper_t f, Res res; ArenaEnter(arena); - AVER(FUNCHECK(f)); - /* p and s are arbitrary closures, hence can't be checked */ + STACK_CONTEXT_BEGIN(arena) { + AVER(FUNCHECK(f)); + /* p and s are arbitrary closures, hence can't be checked */ - AVER(ArenaGlobals(arena)->clamped); /* .assume.parked */ - AVER(arena->busyTraces == TraceSetEMPTY); /* .assume.parked */ + AVER(ArenaGlobals(arena)->clamped); /* .assume.parked */ + AVER(arena->busyTraces == TraceSetEMPTY); /* .assume.parked */ - res = ArenaRootsWalk(ArenaGlobals(arena), f, p, s); - AVER(res == ResOK); + res = ArenaRootsWalk(ArenaGlobals(arena), f, p, s); + AVER(res == ResOK); + } STACK_CONTEXT_END(arena); ArenaLeave(arena); } /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/code/walkt0.c b/mps/code/walkt0.c index 6a378ffe659..172864f3e83 100644 --- a/mps/code/walkt0.c +++ b/mps/code/walkt0.c @@ -14,9 +14,10 @@ #include "mpscams.h" #include "mpscawl.h" #include "mpsclo.h" +#include "mpscsnc.h" #include "mpsavm.h" -#include "mpstd.h" #include "mps.h" +#include "mpm.h" #include /* printf */ @@ -75,7 +76,8 @@ static mps_addr_t make(void) return p; } -/* A stepper function. Passed to mps_arena_formatted_objects_walk. +/* A formatted objects stepper function. Passed to + * mps_arena_formatted_objects_walk. * * Tests the (pool, format) values that MPS passes to it for each * object, by... @@ -88,22 +90,27 @@ static mps_addr_t make(void) * ...2: comparing with what we expect for: * pool * fmt + * + * ...3: accumulating the count and size of objects found */ -struct stepper_data { +typedef struct object_stepper_data { mps_arena_t arena; mps_pool_t expect_pool; mps_fmt_t expect_fmt; - unsigned long count; -}; + size_t count; /* number of non-padding objects found */ + size_t objSize; /* total size of non-padding objects */ + size_t padSize; /* total size of padding objects */ +} object_stepper_data_s, *object_stepper_data_t; -static void stepper(mps_addr_t object, mps_fmt_t format, - mps_pool_t pool, void *p, size_t s) +static void object_stepper(mps_addr_t object, mps_fmt_t format, + mps_pool_t pool, void *p, size_t s) { - struct stepper_data *sd; + object_stepper_data_t sd; mps_arena_t arena; mps_bool_t b; mps_pool_t query_pool; mps_fmt_t query_fmt; + size_t size; Insist(s == sizeof *sd); sd = p; @@ -120,22 +127,48 @@ static void stepper(mps_addr_t object, mps_fmt_t format, Insist(b); Insist(query_fmt == format); Insist(format == sd->expect_fmt); - - sd->count += 1; - return; + + size = AddrOffset(object, dylan_skip(object)); + if (dylan_ispad(object)) { + sd->padSize += size; + } else { + ++ sd->count; + sd->objSize += size; + } } + +/* A roots stepper function. Passed to mps_arena_roots_walk. */ + +typedef struct roots_stepper_data { + mps_root_t exactRoot; + size_t count; +} roots_stepper_data_s, *roots_stepper_data_t; + +static void roots_stepper(mps_addr_t *ref, mps_root_t root, void *p, size_t s) +{ + roots_stepper_data_t data = p; + Insist(ref != NULL); + Insist(p != NULL); + Insist(s == sizeof *data); + Insist(root == data->exactRoot); + ++ data->count; +} + + /* test -- the body of the test */ -static void *test(mps_arena_t arena, mps_pool_class_t pool_class) +static void test(mps_arena_t arena, mps_pool_class_t pool_class) { mps_chain_t chain; mps_fmt_t format; mps_pool_t pool; mps_root_t exactRoot; size_t i; + size_t totalSize, freeSize, allocSize, bufferSize; unsigned long objs; - struct stepper_data sdStruct, *sd; + object_stepper_data_s objectStepperData, *sd; + roots_stepper_data_s rootsStepperData, *rsd; die(dylan_fmt(&format, arena), "fmt_create"); die(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); @@ -175,25 +208,45 @@ static void *test(mps_arena_t arena, mps_pool_class_t pool_class) ++objs; } - sd = &sdStruct; + mps_arena_park(arena); + + rsd = &rootsStepperData; + rsd->exactRoot = exactRoot; + rsd->count = 0; + mps_arena_roots_walk(arena, roots_stepper, rsd, sizeof *rsd); + printf("%lu %lu\n", (unsigned long)rsd->count, (unsigned long)exactRootsCOUNT); + Insist(rsd->count == exactRootsCOUNT); + + sd = &objectStepperData; sd->arena = arena; sd->expect_pool = pool; sd->expect_fmt = format; sd->count = 0; - mps_arena_formatted_objects_walk(arena, stepper, sd, sizeof *sd); - /* Note: stepper finds more than we expect, due to pad objects */ - /* printf("stepper found %ld objs\n", sd->count); */ + sd->objSize = 0; + sd->padSize = 0; + mps_arena_formatted_objects_walk(arena, object_stepper, sd, sizeof *sd); + Insist(sd->count == objs); + totalSize = mps_pool_total_size(pool); + freeSize = mps_pool_free_size(pool); + allocSize = totalSize - freeSize; + bufferSize = AddrOffset(ap->init, ap->limit); + printf("%s: obj=%lu pad=%lu total=%lu free=%lu alloc=%lu buffer=%lu\n", + ClassName(pool_class), + (unsigned long)sd->objSize, + (unsigned long)sd->padSize, + (unsigned long)totalSize, + (unsigned long)freeSize, + (unsigned long)allocSize, + (unsigned long)bufferSize); + Insist(sd->objSize + sd->padSize + bufferSize == allocSize); - 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; } int main(int argc, char *argv[]) @@ -210,9 +263,10 @@ int main(int argc, char *argv[]) test(arena, mps_class_amc()); test(arena, mps_class_amcz()); - /* TODO: test(arena, mps_class_ams()); -- see job003738 */ + test(arena, mps_class_ams()); test(arena, mps_class_awl()); test(arena, mps_class_lo()); + test(arena, mps_class_snc()); mps_thread_dereg(thread); mps_arena_destroy(arena); diff --git a/mps/code/xci3gc.gmk b/mps/code/xci3gc.gmk index 2ba7c8af121..45b1e809c41 100644 --- a/mps/code/xci3gc.gmk +++ b/mps/code/xci3gc.gmk @@ -1,31 +1,32 @@ # -*- makefile -*- # -# xci3gc.gmk: BUILD FOR MACOS X (CARBON)/INTEL IA32/GCC PLATFORM +# xci3gc.gmk: BUILD FOR macOS/IA-32/GCC PLATFORM # # $Id$ -# Copyright (c) 2001,2006 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2001-2018 Ravenbrook Limited. See end of file for license. # # Naively copied from xcppgc.gmk, could do with going over properly. PFM = xci3gc -MPMPF = lockix.c thxc.c vmix.c protix.c proti3.c prmci3xc.c span.c ssixi3.c \ - protxc.c - -LIBS = - -RANLIB=ranlib +MPMPF = \ + lockix.c \ + prmci3.c \ + prmcxc.c \ + prmcxci3.c \ + protix.c \ + protxc.c \ + span.c \ + thxc.c \ + vmix.c include gc.gmk - -CC = gcc -arch i386 - include comm.gmk # C. COPYRIGHT AND LICENSE # -# Copyright (C) 2001-2006 Ravenbrook Limited . +# Copyright (C) 2001-2018 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. # diff --git a/mps/code/xci3ll.gmk b/mps/code/xci3ll.gmk new file mode 100644 index 00000000000..afd9fd1e777 --- /dev/null +++ b/mps/code/xci3ll.gmk @@ -0,0 +1,72 @@ +# -*- makefile -*- +# +# xci3ll.gmk: BUILD FOR macOS/IA-32/Clang/LLVM PLATFORM +# +# $Id$ +# Copyright (c) 2001-2018 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 = xci3ll + +MPMPF = \ + lockix.c \ + prmci3.c \ + prmcxc.c \ + prmcxci3.c \ + protix.c \ + protxc.c \ + span.c \ + thxc.c \ + vmix.c + +include ll.gmk + +CC = clang -arch i386 + +include comm.gmk + + +# C. COPYRIGHT AND LICENSE +# +# Copyright (C) 2001-2018 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/mps/code/xci6gc.gmk b/mps/code/xci6gc.gmk new file mode 100644 index 00000000000..00bd64d855a --- /dev/null +++ b/mps/code/xci6gc.gmk @@ -0,0 +1,69 @@ +# -*- makefile -*- +# +# xci6gc.gmk: BUILD FOR macOS/x86-64/GCC PLATFORM +# +# $Id$ +# Copyright (c) 2001-2018 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 = xci6gc + +MPMPF = \ + lockix.c \ + prmci6.c \ + prmcxc.c \ + prmcxci6.c \ + protix.c \ + protxc.c \ + span.c \ + thxc.c \ + vmix.c + +include gc.gmk +include comm.gmk + + +# C. COPYRIGHT AND LICENSE +# +# Copyright (C) 2001-2018 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/mps/code/xci6ll.gmk b/mps/code/xci6ll.gmk index 597979539ef..1e420f6bd95 100644 --- a/mps/code/xci6ll.gmk +++ b/mps/code/xci6ll.gmk @@ -1,9 +1,9 @@ # -*- makefile -*- # -# xci6ll.gmk: BUILD FOR MAC OS X/x86_64/Clang PLATFORM +# xci6ll.gmk: BUILD FOR macOS/x86-64/Clang/LLVM PLATFORM # # $Id$ -# Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2001-2018 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 @@ -15,12 +15,12 @@ PFM = xci6ll MPMPF = \ lockix.c \ - prmci6xc.c \ - proti6.c \ + prmci6.c \ + prmcxc.c \ + prmcxci6.c \ protix.c \ protxc.c \ span.c \ - ssixi6.c \ thxc.c \ vmix.c @@ -30,7 +30,7 @@ include comm.gmk # C. COPYRIGHT AND LICENSE # -# Copyright (C) 2001-2014 Ravenbrook Limited . +# Copyright (C) 2001-2018 Ravenbrook Limited . # All rights reserved. This is an open source license. Contact # Ravenbrook for commercial licensing options. # diff --git a/mps/code/zcoll.c b/mps/code/zcoll.c index 459f87595f5..87bd39f5cad 100644 --- a/mps/code/zcoll.c +++ b/mps/code/zcoll.c @@ -804,8 +804,13 @@ static void testscriptA(const char *script) printf(" Create arena, size = %lu.\n", arenasize); /* arena */ - die(mps_arena_create(&arena, mps_arena_class_vm(), (size_t)arenasize), - "arena_create"); + MPS_ARGS_BEGIN(args) { + /* Randomize pause time as a regression test for job004011. */ + MPS_ARGS_ADD(args, MPS_KEY_PAUSE_TIME, rnd_pause_time()); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, arenasize); + die(mps_arena_create_k(&arena, mps_arena_class_vm(), args), + "arena_create\n"); + } MPS_ARGS_END(args); /* thr: used to stop/restart multiple threads */ die(mps_thread_reg(&thr, arena), "thread"); diff --git a/mps/configure b/mps/configure index 2aa767cd2d5..8d938b2d09d 100755 --- a/mps/configure +++ b/mps/configure @@ -3513,6 +3513,17 @@ $as_echo "FreeBSD x86" >&6; } CPP="$CC -I/usr/local/include -E" PFMCFLAGS="$CFLAGS_GC" ;; + amd64-*-freebsd*/yes | x86_64-*-freebsd*/yes) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: FreeBSD x86_64" >&5 +$as_echo "FreeBSD x86_64" >&6; } + MPS_OS_NAME=fr + MPS_ARCH_NAME=i6 + MPS_BUILD_NAME=ll + # Need /usr/local/include in order to find sqlite3.h + CFLAGS="-I/usr/local/include" + CPP="$CC -I/usr/local/include -E" + PFMCFLAGS="$CFLAGS_GC" + ;; 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; } @@ -4789,3 +4800,44 @@ fi echo 1>&2 "CONFIGURE/MAKE IS NOT THE BEST WAY TO BUILD THE MPS -- see " + + +# C. COPYRIGHT AND LICENSE +# +# Copyright (C) 2012-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: +# +# 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/mps/configure.ac b/mps/configure.ac index 940ca852cc2..436aad238c6 100644 --- a/mps/configure.ac +++ b/mps/configure.ac @@ -1,7 +1,7 @@ # configure.ac -- autoconf configuration for the MPS -*- Autoconf -*- # # $Id$ -# Copyright (C) 2012-2014 Ravenbrook Limited. See end of file for license. +# Copyright (C) 2012-2016 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. @@ -107,6 +107,7 @@ case $host/$CLANG in CFLAGS="-I/usr/local/include" CPP="$CC -I/usr/local/include -E" PFMCFLAGS="$CFLAGS_GC" + ;; amd64-*-freebsd*/no | x86_64-*-freebsd*/no) AC_MSG_RESULT([FreeBSD x86_64]) MPS_OS_NAME=fr @@ -170,3 +171,44 @@ AC_CONFIG_FILES(Makefile example/scheme/Makefile) AC_OUTPUT echo 1>&2 "CONFIGURE/MAKE IS NOT THE BEST WAY TO BUILD THE MPS -- see " + + +# C. COPYRIGHT AND LICENSE +# +# Copyright (C) 2012-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: +# +# 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/mps/design/abq.txt b/mps/design/abq.txt index e3bd56a9bc8..75aaec3d1a2 100644 --- a/mps/design/abq.txt +++ b/mps/design/abq.txt @@ -93,20 +93,20 @@ If the queue is full, return ``TRUE``, otherwise return ``FALSE``. Return the number of elements in the queue. -``typedef Bool (*ABQVisitor)(Bool *deleteReturn, void *element, void *closureP, Size closureS)`` +``typedef Bool (*ABQVisitor)(Bool *deleteReturn, void *element, void *closure)`` A callback function for ``ABQIterate()``. The parameter ``element`` is -an element in the queue, and ``closureP`` and ``closureS`` are the -values that were originally passed to ``ABQIterate()``. This function -must set ``*deleteReturn`` to ``FALSE`` if ``element`` must be kept in -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``. +an element in the queue, and ``closure`` is the value originally +passed to ``ABQIterate()``. This function must set ``*deleteReturn`` +to ``FALSE`` if ``element`` must be kept in 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, ABQVisitor visitor, void *closureP, Size closureS)`` +``void ABQIterate(ABQ abq, ABQVisitor visitor, void *closure)`` Call ``visitor`` for each element in the queue, passing the element -and ``closureP``. See ``ABQVisitor`` for details. +and ``closure``. See ``ABQVisitor`` for details. Document History @@ -120,8 +120,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2016 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2013-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 diff --git a/mps/design/alloc-frame.txt b/mps/design/alloc-frame.txt index b199c2c567f..c3717da67d3 100644 --- a/mps/design/alloc-frame.txt +++ b/mps/design/alloc-frame.txt @@ -170,11 +170,11 @@ as parameters. _`.op.obligatory`: The following operations are supported on any allocation point which supports allocation frames:- -_`.operation.push`: The ``PushFrame()`` operation creates a new +_`.operation.push`: The *FramePush* operation creates a new allocation frame of the currently chosen frame class, makes this new frame the current frame, and returns a handle for the frame. -_`.operation.pop`: The ``PopFrame()`` operation takes a frame handle +_`.operation.pop`: The *FramePop* operation takes a frame handle as a parameter. Some pool classes might insist or assume that this is the handle for the current frame. It finds the parent of that frame and makes it the current frame. The operation indicates that all @@ -190,23 +190,23 @@ allocation frames, but not all. Pools may choose to support some or all of these operations for certain frame classes. An unsupported operation will return a failure value:- -_`.operation.select`: The ``SelectFrame()`` operation takes a frame +_`.operation.select`: The *FrameSelect* operation takes a frame handle as a parameter and makes that frame the current frame. It does not indicate that any children of the current frame contain objects which are likely to be dead. -_`.operation.select-addr`: The ``SelectFrameOfAddr()`` operation takes +_`.operation.select-addr`: The *FrameSelectOfAddr* operation takes an address as a parameter and makes the frame of that address the current frame. It does not indicate that any children of the current frame contain objects which are likely to be dead. -_`.operation.in-frame`: The ``AddrInFrame()`` operation determines +_`.operation.in-frame`: The *FrameHasAddr* operation determines whether the supplied address is the address of an object allocated in the supplied frame, or any child of that frame. -_`.operation.set`: The ``SetFrameClass()`` operation takes a frame +_`.operation.set`: The *SetFrameClass* operation takes a frame class and an allocation point as parameters, and makes that the -current frame class for the allocation point. The next ``PushFrame()`` +current frame class for the allocation point. The next *FramePush* operation will create a new frame of that class. @@ -231,32 +231,32 @@ External functions .................. _`.fn.client.push`: ``mps_ap_frame_push()`` is used by clients to -invoke the ``PushFrame()`` operation. For lightweight frames, this +invoke the *FramePush* operation. For lightweight frames, this might not invoke the corresponding internal function. _`.fn.client.pop`: ``mps_ap_frame_pop()`` is used by clients to invoke -the ``PopFrame()`` operation. For lightweight frames, this might not +the *FramePop* operation. For lightweight frames, this might not invoke the corresponding internal function. ``mps_res_t mps_ap_frame_select(mps_ap_t buf, mps_frame_t frame)`` _`.fn.client.select`: This following function is used by clients to -invoke the ``SelectFrame()`` operation. +invoke the *FrameSelect* operation. ``mps_res_t mps_ap_frame_select_from_addr(mps_ap_t buf, mps_addr_t addr)`` _`.fn.client.select-addr`: This function is used by clients to invoke -the ``SelectFrameOfAddr()`` operation. +the *FrameSelectOfAddr* operation. ``mps_res_t mps_ap_addr_in_frame(mps_bool_t *inframe_o, mps_ap_t buf, mps_addr_t *addrref, mps_frame_t frame)`` _`.fn.client.in-frame`: This function is used by clients to invoke the -``AddrInFrame()`` operation. +*FrameHasAddr* operation. ``mps_res_t mps_ap_set_frame_class(mps_ap_t buf, mps_frame_class_t class)`` _`.fn.client.set`: This function is used by clients to invoke the -``SetFrameClass()`` operation. +*SetFrameClass* operation. ``mps_frame_class_t mps_alloc_frame_class_stack(void)`` @@ -278,32 +278,32 @@ _`.type.frame-class`: Frame classes are defined as an abstract type. ``typedef Res (*PoolFramePushMethod)(AllocFrame *frameReturn, Pool pool, Buffer buf)`` _`.fn.push`: A pool method of this type is called (if needed) to -invoke the ``PushFrame()`` operation. +invoke the *FramePush* operation. ``typedef Res (*PoolFramePopMethod)(Pool pool, Buffer buf, AllocFrame frame)`` _`.fn.pop`: A pool method of this type is called (if needed) -to invoke the PopFrame operation: +to invoke the *FramePop* operation: ``typedef Res (*PoolFrameSelectMethod)(Pool pool, Buffer buf, AllocFrame frame)`` _`.fn.select`: A pool method of this type is called to invoke the -``SelectFrame()`` operation. +*FrameSelect* operation. ``typedef Res (*PoolFrameSelectFromAddrMethod)(Pool pool, Buffer buf, Addr addr)`` _`.fn.select-addr`: A pool method of this type is called to invoke the -``SelectFrameOfAddr()`` operation. +*FrameSelectOfAddr* operation. -``typedef Res (*PoolAddrInFrameMethod)(Bool *inframeReturn, Pool pool, Seg seg, Addr *addrref, AllocFrame frame)`` +``typedef Res (*PoolFrameHasAddrMethod)(Bool *inframeReturn, Pool pool, Seg seg, Addr *addrref, AllocFrame frame)`` _`.fn.in-frame`: A pool method of this type is called to invoke the -``AddrInFrame()`` operation. +*FrameHasAddr* operation. ``typedef Res (*PoolSetFrameClassMethod)(Pool pool, Buffer buf, AllocFrameClass class)`` _`.fn.set`: A pool method of this type is called to invoke the -``SetFrameClass()`` operation. +*SetFrameClass* operation. Lightweight frames @@ -313,96 +313,23 @@ Overview ........ _`.lw-frame.overview`: Allocation points provide direct support for -lightweight frames, and are designed to permit PushFrame and PopFrame -operations without the need for locking and delegation to the pool -method. Pools can disable this mechanism for any allocation point, so -that the pool method is always called. The pool method will be called -whenever synchronization is required for other reasons (e.g. the -buffer is tripped). +lightweight frames, and are designed to permit *FramePush* and +*FramePop* operations without the need for locking and delegation to +the pool method. The pool method will be called whenever +synchronization is required for other reasons (e.g. the buffer is +tripped). _`.lw-frame.model`: Lightweight frames offer direct support for a -particular model of allocation frame use, whereby the PushFrame +particular model of allocation frame use, whereby the *FramePush* operation returns the current allocation pointer as a frame handle, -and the PopFrame operation causes the allocation pointer to be reset +and the *FramePop* operation causes the allocation pointer to be reset to the address of the frame handle. This model should be suitable for -simple stack frames, where more advanced operations like SelectFrame +simple stack frames, where more advanced operations like *FrameSelect* are not supported. It may also be suitable for more advanced allocation frame models when they are being used simply. The use of a complex operation always involves synchronization via locking, and the pool may disable lightweight synchronization temporarily at this time. -State -..... - -_`.lw-frame.states`: Allocation points supporting lightweight frames -will be in one of the following states: - -============ ================================================================ -Valid Indicates that ``PushFrame()`` can be a lightweight - operation and need not be synchronized. -PopPending Indicates that there has been a ``PopFrame()`` operation - that the pool must respond to. -Disabled Indicates that the pool has disabled support for lightweight - operations for this AP. -============ ================================================================ - -These states are in addition to the state normally held by an AP for -allocation purposes. An AP will be in the Disabled state at creation. - -_`.lw-frame.transitions`: State transitions happen under the following -circumstances: - -======================= ==================================================== -Valid → PopPending As a result of a client ``PopFrame()`` - operation. -Valid → Disabled At the choice of the pool (for example, when - responding to a ``SelectFrame()`` operation). -PopPending → Valid At the choice of the pool, when processing a - ``PopFrame()``. -PopPending → Disabled At the choice of the pool, when processing a - ``PopFrame()``. -Disabled → Valid At the choice of the pool. -Disabled → Popframe Illegal. -======================= ==================================================== - -_`.lw-frame.state-impl`: Each AP contains 3 additional fields to hold this state:: - - mps_addr_t frameptr; - mps_bool_t enabled; - mps_bool_t lwPopPending; - -_`.lw-frame.enabled`: The ``enabled`` slot holds the following values for -each state: - -========== ========== -Valid ``TRUE`` -PopPending ``TRUE`` -Disabled ``FALSE`` -========== ========== - -_`.lw-frame.frameptr`: The ``frameptr`` slot holds the following values -for each state: - -========== ============================================ -Valid ``NULL`` -PopPending Frame handle for most recently popped frame. -Disabled ``NULL`` -========== ============================================ - -_`.lw-frame.lwPopPending`: The ``lwPopPending`` slot holds the -following values for each state: - -========== ========= -Valid ``FALSE`` -PopPending ``TRUE`` -Disabled ``FALSE`` -========== ========= - -_`.lw-frame.state-for-gc`: It is not necessary for the tracer, format -code, pool, or any other part of the GC support in MPS to read either -of the two additional AP fields in order to scan a segment which -supports a lightweight allocation frame. - Synchronization ............... @@ -414,49 +341,51 @@ operation on an AP may only be performed by a single mutator thread at a time. Each of the operations on allocation frames counts as an operation on an AP. -_`.lw-frame.sync.pool`: Pools are permitted to read or modify the -lightweight frame state of an AP only in response to an operation on -that AP. - -_`.lw-frame.sync.external`: The external functions -``mps_ap_frame_push()`` and ``mps_ap_frame_pop()`` are permitted to -read the values of the ``enabled`` and ``frameptr`` fields for the -supplied AP without claiming the arena lock. They are permitted to -modify the ``frameptr`` field if and only if ``enabled == FALSE``. - -_`.lw-frame.sync.trip`: When a buffer trip happens, and the trap -wasn't set by MPS itself (that is, it wasn't because of a flip or for -logging), then the buffer code must check whether the AP has state -PopPending. If it does, the buffer code must call the Pool. - Implementation .............. -_`.lw-frame.push`: The external ``PushFrame()`` operation -(``mps_ap_frame_push()``) performs the following operations:: +_`.lw-frame.push`: The external *FramePush* operation +``mps_ap_frame_push()`` performs the following operations:: - IF (!APIsTrapped(ap) && StateOfFrame(ap) == Valid && ap->init == ap->alloc) + IF ap->init != ap->alloc + FAIL + ELSE IF ap->init < ap->limit *frame_o = ap->init; ELSE WITH_ARENA_LOCK - PerformInternalPushFrameOperation(...) + PerformInternalFramePushOperation(...) END END -_`.lw-frame.pop`: The external ``PopFrame()`` operation +_`.lw-frame.push.limit`: The reason for testing ``ap->init < +ap->limit`` and not ``ap->init <= ap->limit`` is that a frame pointer +at the limit of a buffer (and possibly therefore of a segment) would +be ambiguous: is it at the limit of the segment, or at the base of the +segment that's adjacent in memory? The internal operation must handle +this case, for example by refilling the buffer and setting the frame +at the beginning. + +_`.lw-frame.pop`: The external *FramePop* operation (``mps_ap_frame_pop()``) performs the following operations:: - IF (StateOfFrame(ap) != Disabled) - TrapAP(ap); /* ensure next allocation or push involves the pool */ - ap->frameptr = frame; - ap->lwpopPending = TRUE; + IF ap->init != ap->alloc + FAIL + ELSE IF BufferBase(ap) <= frame AND frame < ap->init + ap->init = ap->alloc = frame; ELSE WITH_ARENA_LOCK - PerformInternalPopFrameOperation(...) + PerformInternalFramePopOperation(...) END END +_`.lw-frame.pop.buffer`: The reason for testing that ``frame`` is in +the buffer is that if it's not, then we're popping to an address in +some other segment, and that means that some objects in the other +segment (and all objects in any segments on the stack in between) are +now dead, and the only way for the pool to mark them as being dead is +to do a heavyweight pop. + Document History ---------------- @@ -473,8 +402,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +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 diff --git a/mps/design/an.txt b/mps/design/an.txt index 7d6cc117407..2f279a6affb 100644 --- a/mps/design/an.txt +++ b/mps/design/an.txt @@ -100,14 +100,14 @@ _`.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.prmc`: 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.ss`: Stack scanning. See design.mps.stack-scan_. + _`.mod.th`: Thread manager. See design.mps.thread-manager_. _`.mod.vm`: Virtual mapping. See design.mps.vm_. @@ -116,7 +116,7 @@ _`.mod.vm`: Virtual mapping. See design.mps.vm_. .. _design.mps.prot: prot .. _design.mps.prmc: prmc .. _design.mps.sp: sp -.. _design.mps.ss: ss +.. _design.mps.stack-scan: stack-scan .. _design.mps.thread-manager: thread-manager .. _design.mps.vm: vm @@ -136,15 +136,15 @@ design.mps.prmc.impl.an.fault_) and requires a single-threaded mutator _`.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 +implementations of the 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.stack-scan`: Assumes that the stack grows downwards and that +``setjmp()`` reliably captures the registers (see design.mps.stack-scan.sol.stack.platform_). _`.lim.th`: Requires a single-threaded mutator (see design.mps.thread-manager.impl.an.single_). @@ -153,15 +153,15 @@ _`.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 +.. _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.stack-scan.sol.stack.platform: stack-scan#sol-stack-platform +.. _design.mps.thread-manager.impl.an.single: thread-manager#impl-an-single +.. _design.mps.vm.impl.an.reserve: vm#impl-an-reserve @@ -176,8 +176,8 @@ Document History Copyright and License --------------------- -Copyright © 2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2014-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 diff --git a/mps/design/arena.txt b/mps/design/arena.txt index 2a94bb05b39..aac6d74f3f3 100644 --- a/mps/design/arena.txt +++ b/mps/design/arena.txt @@ -47,10 +47,9 @@ 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. +which segment contains it, 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. @@ -202,12 +201,6 @@ 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 -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 ``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 @@ -228,10 +221,6 @@ implementation) and the ``extend``, ``retract`` and ``spareCommitExceeded`` methods which have non-callable methods for the benefit of arena classes which don't implement these features. -_`.class.abstract.null`: The abstract class does not provide dummy -implementations of those methods which must be overridden. Instead -each abstract method is initialized to ``NULL``. - Chunks ...... @@ -239,7 +228,8 @@ 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.tracts`: A chunk contains a table of tracts. See +`.tract.table`_. _`.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. @@ -288,7 +278,6 @@ _`.tract.structure`: The tract structure definition looks like this:: PagePoolUnion pool; /* MUST BE FIRST (design.mps.arena.tract.field.pool) */ void *p; /* pointer for use of owning pool */ Addr base; /* Base address of the tract */ - TraceSet white : TRACE_MAX; /* traces for which tract is white */ BOOLFIELD(hasSeg); /* does tract have a seg in p? */ } TractStruct; @@ -318,18 +307,11 @@ 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 +.. _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. -_`.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_). - -.. _design.mps.trace.fix: trace#fix - _`.tract.limit`: The limit of the tract's memory may be determined by adding the arena grain size to the base address. @@ -447,6 +429,17 @@ than the amount of spare committed memory (stored in ``spareCommitExceeded`` is called. +Pause time control +.................. + +_`.pause-time`: The generic arena structure contains the field +``pauseTime`` for the maximum time any operation in the arena may take +before returning to the mutator. This value is used by +``PolicyPollAgain()`` to decide whether to do another unit of tracing +work. The MPS interface provides getter (``mps_arena_pause_time()``) +and setter (``mps_arena_pause_time_set()``) functions. + + Locks ..... @@ -499,7 +492,7 @@ Implementation Tract cache ........... -_`.tract.cache`: When tracts are allocated to pools by ``ArenaAlloc()``, +_`.impl.tract.cache`: When tracts are allocated to pools by ``ArenaAlloc()``, the first tract of the block and it's base address are cached in arena fields ``lastTract`` and ``lastTractBase``. The function ``TractOfBaseAddr()`` (see design.mps.arena.tract-iter.if.block-base(0)) @@ -508,69 +501,74 @@ a cache miss. This optimizes for the common case where a pool allocates a block and then iterates over all its tracts (for example, to attach them to a segment). -_`.tract.uncache`: When blocks of memory are freed by pools, -``ArenaFree()`` checks to see if the cached value for the most recently -allocated tract (see `.tract.cache`_) is being freed. If so, the cache -is invalid, and must be reset. The ``lastTract`` and ``lastTractBase`` -fields are set to ``NULL``. +_`.impl.tract.uncache`: When blocks of memory are freed by pools, +``ArenaFree()`` checks to see if the cached value for the most +recently allocated tract (see `.impl.tract.cache`_) is being freed. If +so, the cache is invalid, and must be reset. The ``lastTract`` and +``lastTractBase`` fields are set to ``NULL``. Control pool ............ -_`.pool.init`: The control pool is initialized by a call to +_`.impl.pool.init`: The control pool is initialized by a call to ``PoolInit()`` during ``ArenaCreate()``. -_`.pool.ready`: All the other fields in the arena are made checkable -before calling ``PoolInit()``, so ``PoolInit()`` can call +_`.impl.pool.ready`: All the other fields in the arena are made +checkable before calling ``PoolInit()``, so ``PoolInit()`` can call ``ArenaCheck(arena)``. The pool itself is, of course, not checkable, so we have a field ``arena->poolReady``, which is false until after -the return from ``PoolInit()``. ``ArenaCheck()`` only checks the pool if -``poolReady``. +the return from ``PoolInit()``. ``ArenaCheck()`` only checks the pool +if ``poolReady``. Traces ...... -_`.trace`: ``arena->trace[ti]`` is valid if and only if +_`.impl.trace`: ``arena->trace[ti]`` is valid if and only if ``TraceSetIsMember(arena->busyTraces, ti)``. -_`.trace.create`: Since the arena created by ``ArenaCreate()`` has -``arena->busyTraces = TraceSetEMPTY``, none of the traces are +_`.impl.trace.create`: Since the arena created by ``ArenaCreate()`` +has ``arena->busyTraces = TraceSetEMPTY``, none of the traces are meaningful. -_`.trace.invalid`: Invalid traces have signature ``SigInvalid``, which -can be checked. +_`.impl.trace.invalid`: Invalid traces have signature ``SigInvalid``, +which can be checked. Polling ....... -_`.poll.fields`: There are three fields of a arena used for polling: -``pollThreshold``, ``insidePoll``, and ``clamped`` (see above). -``pollThreshold`` is the threshold for the next poll: it is set at the -end of ``ArenaPoll()`` to the current polling time plus +_`.impl.poll.fields`: There are three fields of a arena used for +polling: ``pollThreshold``, ``insidePoll``, and ``clamped`` (see +above). ``pollThreshold`` is the threshold for the next poll: it is +set at the end of ``ArenaPoll()`` to the current polling time plus ``ARENA_POLL_MAX``. Location dependencies ..................... -_`.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. +_`.impl.ld`: The ``historyStruct`` contains fields used to maintain a +history of garbage collection and in particular object motion in order +to implement location dependency. -_`.ld.history`: ``arena->history`` is a circular buffer of +_`.impl.ld.epoch`: The ``epoch`` is the "current epoch". This is the number +of "flips" of traces, in which objects might have moved, in the arena +since it was created. From the mutator's point of view, locations +change atomically at flip. + +_`.impl.ld.history`: The ``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 % LDHistoryLENGTH] + history->history[e % LDHistoryLENGTH] is a summary of (the original locations of) objects moved since epoch ``e``. -_`.ld.prehistory`: ``arena->prehistory`` is a ``RefSet`` summarizing +_`.impl.ld.prehistory`: The ``prehistory`` is a ``RefSet`` summarizing the original locations of all objects ever moved. When considering whether a really old location dependency is stale, it is compared with this summary. @@ -579,7 +577,7 @@ this summary. Roots ..... -_`.root-ring`: The arena holds a member of a ring of roots in the +_`.impl.root-ring`: The arena holds a member of a ring of roots in the arena. It holds an incremental serial which is the serial of the next root. @@ -598,6 +596,9 @@ Document History - 2014-02-17 RB_ Updated first field of tract structure. +- 2016-04-08 RB_ All methods in the abstract arena class now have + dummy implementations, so that the class passes its own check. + .. _RB: http://www.ravenbrook.com/consultants/rb/ .. _GDR: http://www.ravenbrook.com/consultants/gdr/ @@ -605,8 +606,8 @@ Document History Copyright and License --------------------- -Copyright © 2001-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2001-2018 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 diff --git a/mps/design/arenavm.txt b/mps/design/arenavm.txt index d746f18d883..cf37391749c 100644 --- a/mps/design/arenavm.txt +++ b/mps/design/arenavm.txt @@ -52,7 +52,7 @@ condemned sets to coincide with zone stripes. _`.overview.gc.tract`: A fast translation from addresses to tract. (See design.mps.arena.req.fun.trans_.) -.. _design.mps.arena.req.fun.trans: arena#req.fun.trans +.. _design.mps.arena.req.fun.trans: arena#req-fun-trans Notes @@ -63,7 +63,7 @@ _`.note.refset`: Some of this document simply assumes that RefSets 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 +.. _design.mps.arena.req.fun.set: arena#req-fun-set Requirements @@ -186,7 +186,7 @@ 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: 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_). @@ -226,7 +226,7 @@ Document History - 2013-05-24 GDR_ Converted to reStructuredText. - 2014-02-17 RB_ Updated to note use of SparseArray rather than direct -management of page table mapping. + management of page table mapping. .. _RB: http://www.ravenbrook.com/consultants/rb/ .. _GDR: http://www.ravenbrook.com/consultants/gdr/ @@ -235,8 +235,8 @@ management of page table mapping. Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2013-2018 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 diff --git a/mps/design/bt.txt b/mps/design/bt.txt index bc069efd2a9..4d6a40b8460 100644 --- a/mps/design/bt.txt +++ b/mps/design/bt.txt @@ -751,8 +751,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +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 diff --git a/mps/design/buffer.txt b/mps/design/buffer.txt index ae55fa34204..d6fee9efcc3 100644 --- a/mps/design/buffer.txt +++ b/mps/design/buffer.txt @@ -182,7 +182,7 @@ 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_. -.. _design.mps.protocol.int.define-special: 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 @@ -212,13 +212,6 @@ class-specific behaviour. _`.replay.init`: The ``init()`` method should emit a ``BufferInit`` event (if there aren't any extra parameters, `` = ""``). -``typedef void (*BufferFinishMethod)(Buffer buffer)`` - -_`.class.method.finish`: ``finish()`` is a class-specific finish -method called from ``BufferFinish()``. Client-defined methods must -call their superclass method (via a next-method call) after performing -any class-specific behaviour. - ``typedef void (*BufferAttachMethod)(Buffer buffer, Addr base, Addr limit, Addr init, Size size)`` _`.class.method.attach`: ``attach()`` is a class-specific method @@ -256,13 +249,6 @@ 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, Count depth)`` - -_`.class.method.describe`: ``describe()`` is a class-specific method -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. - Logging ------- @@ -623,7 +609,7 @@ design.mps.thread-safety_). Returns the pool to which a buffer is attached. -``Res BufferReserve(Addr *pReturn, Buffer buffer, Size size, Bool withReservoirPermit)`` +``Res BufferReserve(Addr *pReturn, Buffer buffer, Size size)`` _`.method.reserve`: Reserves memory from an allocation buffer. @@ -638,7 +624,7 @@ pool to which the buffer belongs) and then passed to the applied twice to a buffer without a ``BufferCommit()`` in-between. In other words, Reserve/Commit pairs do not nest. -``Res BufferFill(Addr *pReturn, Buffer buffer, Size size, Bool withReservoirPermit)`` +``Res BufferFill(Addr *pReturn, Buffer buffer, Size size)`` _`.method.fill`: Refills an empty buffer. If there is not enough space in a buffer to allocate in-line, ``BufferFill()`` must be called to @@ -736,8 +722,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +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 diff --git a/mps/design/cbs.txt b/mps/design/cbs.txt index f004eedd90a..f46160e027d 100644 --- a/mps/design/cbs.txt +++ b/mps/design/cbs.txt @@ -62,40 +62,31 @@ External types ``typedef struct CBSStruct *CBS`` _`.type.cbs`: The type of coalescing block structures. A ``CBSStruct`` -may be embedded in another structure, or you can create it using -``LandCreate()``. +is typically embedded in another structure. -External functions -.................. +External classes +................ -``LandClass CBSLandClassGet(void)`` +_`.class.cbs`: ``CLASS(CBS)`` is the CBS class, a subclass of +``CLASS(Land)`` suitable for passing to ``LandInit()``. -_`.function.class`: The function ``CBSLandClassGet()`` returns the CBS -class, a subclass of ``LandClass`` suitable for passing to -``LandCreate()`` or ``LandInit()``. - -``LandClass CBSFastLandClassGet(void)`` - -_`.function.class.fast`: Returns a subclass of ``CBSLandClass`` that +_`.class.fast`: ``CLASS(CBSFast)`` is subclass of ``CLASS(CBS)`` that maintains, for each subtree, the size of the largest block in that subtree. This enables the ``LandFindFirst()``, ``LandFindLast()``, and ``LandFindLargest()`` generic functions. -``LandClass CBSZonedLandClassGet(void)`` - -_`.function.class.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. - +_`.class.zoned`: ``CLASS(CBSZoned)`` is a subclass of +``CLASS(CBSFast)`` that maintains, for each subtree, the union of the +zone sets of all ranges in that subtree. This enables the +``LandFindInZones()`` generic function. Keyword arguments ................. -When initializing a CBS, ``LandCreate()`` and ``LandInit()`` take the -following optional keyword arguments: +When initializing a CBS, ``LandInit()`` takes the following optional +keyword arguments: * ``CBSBlockPool`` (type ``Pool``) is the pool from which the CBS block descriptors will be allocated. If omitted, a new MFS pool is @@ -111,7 +102,9 @@ following optional keyword arguments: 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.) + memory. See design.mps.bootstrap.land.sol.pool_.) + + .. _design.mps.bootstrap.land.sol.pool: bootstrap#land-sol-pool Limitations @@ -142,30 +135,41 @@ Splay tree _`.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. +structure with a semi-open address range (design.mps.range_). The +splay tree is ordered by the range base address. .. _design.mps.splay: splay +.. _design.mps.range: range _`.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. +block, and very rapid failure if there is no suitable block. For +example, this is used in the implementation of allocation in the MVFF +pool class (design.mps.poolmvff_). + +.. _design.mps.poolmvff: poolmvff _`.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()``. For +example, this is used in the implementation of allocation buffers in +the MVFF pool class (design.mps.poolmvff_). _`.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. +location of a block in a set of zones. For example, this is used to +allocate segments in particular zones in the arena to optimised +garbage collection (see design.mps.critical-path_). + +.. _design.mps.critical-path: critical-path Low memory behaviour @@ -182,11 +186,11 @@ CBS or deleted from it, and the call to ``LandInsert()`` or The CBS block ............. -_`.impl.cbs.block`: The block contains a base-limit pair and a splay +_`.impl.cbs.block`: The block contains a non-empty range and a splay tree node. -_`.impl.cbs.block.special`: The base and limit may be equal if the -block is halfway through being deleted. +_`.impl.cbs.block.special`: The range may be empty if the block is +halfway through being deleted. _`.impl.cbs.block.special.just`: This conflates values and status, but is justified because block size is very important. @@ -226,9 +230,21 @@ 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). +implementation for the ``LandIterateAndDelete()`` generic function +using ``TreeTraverseAndDelete()``, which calls ``TreeToVine()`` first, +iterates over the vine (where deletion is straightforward), and then +rebalances the tree. Note that this is little better than using +``SplayFirst()`` and ``SplayNext()``. + +_`.future.lazy-coalesce`: It's long been observed that small blocks +are often freed and then reallocated, so that coalescing them is a +waste of time. It might be worth considering how a splay tree could +implement a lazy coalescing scheme, where blocks are coalesced with +their adjacent neighbours during the search only if they aren't big +enough. This would break `.impl.find-largest`_ and so might be best +done as a different kind of land. On the other hand, since the MPS +does not use client memory to store the tree, eager coalescing avoids +allocation. Risks @@ -275,6 +291,10 @@ Document History - 2014-04-01 GDR_ Moved generic material to design.mps.land_. Documented new keyword arguments. +- 2016-03-27 RB_ Adding cross references to usage. Updating future + with reference to ``TreeTraverseAndDelete()``. Adding future idea + about lazy coalescing. + .. _RB: http://www.ravenbrook.com/consultants/rb/ .. _GDR: http://www.ravenbrook.com/consultants/gdr/ @@ -282,7 +302,7 @@ Document History Copyright and License --------------------- -Copyright © 1998-2016 Ravenbrook Limited. All rights reserved. +Copyright © 1998-2018 Ravenbrook Limited. All rights reserved. . This is an open source license. Contact Ravenbrook for commercial licensing options. diff --git a/mps/design/check.txt b/mps/design/check.txt index 138c2a7fc1d..5a97b7f3e21 100644 --- a/mps/design/check.txt +++ b/mps/design/check.txt @@ -122,6 +122,18 @@ reference this tag. The structure could be considered for addition to ``mpmst.h``. +Common assertions +----------------- + +_`.common`: Some assertions are commonly triggered by mistakes in the +client program. These are listed in the section "Common assertions and +their causes" in the MPS Reference, together with an explanation of +their likely cause, and advice for fixing the problem. To assist with +keeping the MPS Reference up to date, these assertions are marked with +a cross-reference to this tag. When you update the assertion, you must +also update the MPS Reference. + + Document History ---------------- @@ -138,8 +150,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2013-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 diff --git a/mps/design/class-interface.txt b/mps/design/class-interface.txt deleted file mode 100644 index 83e839cdc2f..00000000000 --- a/mps/design/class-interface.txt +++ /dev/null @@ -1,304 +0,0 @@ -.. mode: -*- rst -*- - -Pool class interface -==================== - -:Tag: design.mps.class-interface -:Author: Richard Brooksby -:Date: 1997-08-19 -:Status: incomplete design -:Revision: $Id$ -:Copyright: See `Copyright and License`_. -:Index terms: pair: class interface; design - - -Introduction -------------- - -_`.intro`: This document describes the interface and protocols between -the MPM and the pool class implementations. - -.. note:: - - This document should be merged into design.mps.pool_. Pekka P. - Pirinen, 1999-07-20. - - .. _design.mps.pool: pool - - -Fields ------- - -_`.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. - -_`.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". - -_`.field.attr`: The ``attr`` field must be a bitset of pool class -attributes. See design.mps.type.attr_. - -.. _design.mps.type.attr: type - -_`.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)``. - -_`.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. - - -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. 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 -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. - - -Text ------ - -_`.alloc.size`: The pool class implementation defines the meaning of -the "size" parameter to the ``alloc`` and ``free`` methods. It may not -actually correspond to a number of bytes of memory. - -_`.alloc.size.align`: In particular, the class may allow an unaligned -size to be passed. - - -Document history ----------------- - -- 1997-08-19 RB_ Initial draft. David Jones added comments about how - accurate this document is. - -- 2002-06-07 RB_ Converted from MMInfo database design document. - -- 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/ - - -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/mps/design/clock.txt b/mps/design/clock.txt index 9cb4c683049..3213d6ccff5 100644 --- a/mps/design/clock.txt +++ b/mps/design/clock.txt @@ -7,7 +7,7 @@ Fast high-resolution clock :Author: Gareth Rees :Date: 2016-03-06 :Status: complete design -:Revision: $Id: //info.ravenbrook.com/project/mps/master/design/abq.txt#5 $ +:Revision: $Id$ :Copyright: See section `Copyright and License`_. :Index terms: pair: clock; design @@ -93,8 +93,8 @@ Document History Copyright and License --------------------- -Copyright © 2016 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 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 diff --git a/mps/design/collection.txt b/mps/design/collection.txt index ebf0c05df9f..9adea762c5e 100644 --- a/mps/design/collection.txt +++ b/mps/design/collection.txt @@ -419,8 +419,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +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 diff --git a/mps/design/config.txt b/mps/design/config.txt index 9d49a5889ce..3bf1d96e29c 100644 --- a/mps/design/config.txt +++ b/mps/design/config.txt @@ -542,7 +542,8 @@ 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``. +the generic ("ANSI") lock module ``lockan.c`` can be used instead of +the platform-specific lock module. _`.opt.poll`: ``CONFIG_POLL_NONE`` causes the MPS to be built without support for polling. This means that garbage collections will only @@ -550,6 +551,16 @@ 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``. +_`.opt.signal.suspend`: ``CONFIG_PTHREADEXT_SIGSUSPEND`` names the +signal used to suspend a thread, on platforms using the POSIX thread +extensions module. See design.pthreadext.impl.signals_. + +.. _design.pthreadext.impl.signals: pthreadext#impl.signals + +_`.opt.signal.resume`: ``CONFIG_PTHREADEXT_SIGRESUME`` names the +signal used to resume a thread, on platforms using the POSIX thread +extensions module. See design.pthreadext.impl.signals_. + To document ----------- @@ -610,8 +621,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2016 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2013-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 diff --git a/mps/design/critical-path.txt b/mps/design/critical-path.txt index 6576ca3760e..4a02379d943 100644 --- a/mps/design/critical-path.txt +++ b/mps/design/critical-path.txt @@ -88,11 +88,11 @@ operation is to look up the segment pointed to by the pointer and see if it was condemned. This is a fast lookup. After that, each pool class must decide whether the pointer is to a -condemned object and do something to preserve it. This code is still -critical. The MPS will have tried to condemn objects that are dead, but -those objects are still likely to be in segments with other objects that -must be preserved. The pool class fix method must quickly distinguish -between them. +condemned object and do something to preserve it. This code is still +critical. The MPS will have tried to condemn objects that are dead, +but those objects are still likely to be in segments with other +objects that must be preserved. The segment class fix method must +quickly distinguish between them. Furthermore, many objects will be preserved at least once in their lifetime, so even the code that preserves an object needs to be highly @@ -116,7 +116,7 @@ Very briefly, the critical path consists of five stages: write a good scanner. Then that could be linked from here. #. The first-stage fix, which filters out pointers inline in the - scanner. This is implemented in ``MPS_FIX()`` macros in + scanner. This is implemented in the ``MPS_FIX1()`` macro in mps.h_. .. _mps.h: ../code/mps.h @@ -126,10 +126,10 @@ Very briefly, the critical path consists of five stages: .. _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. - +#. The third-stage fix, which filters out pointers using + segment-specific information. Implemented in segment class + functions called ``amcSegFix()``, ``loSegFix()``, etc. in pool*.c. + #. Preserving the object, which might entail: - marking_ it to prevent it being recycled; and/or @@ -249,16 +249,15 @@ 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 -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 pointer is in a tract allocated with garbage collected objects, +then the table also contains a pointer to a "segment", which contains +a bitfield representing the "white set"---the set of garbage +collection traces for which the tract is "interesting". If a segment +isn't interesting, then we know that it contains no condemned objects, +and we can filter out the pointer. -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. +The MPM can't know anything about the internal layout of the segment, +so at this point we dispatch to the third stage fix. This dispatch is slightly subtle. We have a cache of the function to dispatch to in the scan state, which has recently been looked at and @@ -266,36 +265,30 @@ is with luck still in the processor cache. The reason there is a dispatch at all is to allow for a fast changeover to emergency garbage collection, or overriding of garbage collection with extra operations. Those are beyond the scope of this document. Normally, ``ss->fix`` -points at ``PoolFix()``, and we rely somewhat on modern processor -`branch target prediction -`_). -``PoolFix()`` is passed the pool, which is fetched from the tract -table entry, and that should be in the cache. +points at ``SegFix()``. -``PoolFix()`` itself dispatches to the pool class. Normally, a -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 if that would -improve performance. +``SegFix()`` is passed the segment, which is fetched from the tract +table entry, and that should be in the cache. ``SegFix()`` itself +dispatches to the segment class. -The third stage fix in the pool class -------------------------------------- -The final stage of fixing is entirely dependent on the pool class. The -MPM can't, in general, know how the objects within a pool are arranged, -so this is pool class specific code. +The third stage fix in the segment class +---------------------------------------- +The final stage of fixing is entirely dependent on the segment class. +The MPM can't, in general, know how the objects within a segment are +arranged, so this is segment class specific code. -Furthermore, the pool class must make decisions based on the "reference -rank" of the pointer. If a pointer is ambiguous (``RankAMBIG``) then it -can't be changed, so even a copying pool class can't move an object. -On the other hand, if the pointer is weak (``RankWEAK``) then the pool fix -method shouldn't preserve the object at all, even if it's condemned. +Furthermore, the segment class must make decisions based on the +"reference rank" of the pointer. If a pointer is ambiguous +(``RankAMBIG``) then it can't be changed, so even a copying segment +class can't move an object. On the other hand, if the pointer is weak +(``RankWEAK``) then the segment fix method shouldn't preserve the +object at all, even if it's condemned. -The exact details of the logic that the pool fix must implement in +The exact details of the logic that the segment fix must implement in order to co-operate with the MPM and other pools are beyond the scope of this document, which is about the critical path. Since it is on -the critical path, it's important that whatever the pool fix does is +the critical path, it's important that whatever the segment fix does is simple and fast and returns to scanning as soon as possible. The first step, though, is to further filter out pointers which aren't @@ -308,23 +301,23 @@ 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.) +As a simple example, ``loSegFix()`` is the segment fix method for +segments belonging to 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 -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. +``loSegFix()`` filters any ambiguous pointers that aren't aligned, +since they can't point to objects it allocated. Otherwise it subtracts +the 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 ``loSegReclaim()`` is called later on. +``loSegFix()`` illustrates about the minimum and most efficient thing +a segment fix method can do. Other considerations @@ -377,7 +370,7 @@ Document History Copyright and License --------------------- -Copyright © 2012-2014 Ravenbrook Limited. All rights reserved. +Copyright © 2012-2018 Ravenbrook Limited. All rights reserved. . This is an open source license. Contact Ravenbrook for commercial licensing options. diff --git a/mps/design/diag.txt b/mps/design/diag.txt index f8e86e2bdda..f4da8c41724 100644 --- a/mps/design/diag.txt +++ b/mps/design/diag.txt @@ -78,7 +78,7 @@ There are two mechanism for getting diagnostic output: 0x00007fff83e42d46 in __kill () (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); + 711 Res res = CBSInsert(MVTCBS(mvt), base, limit); (gdb) p MVTDescribe(mvt, mps_lib_get_stdout(), 0) MVT 0000000103FFE160 { minSize: 8 @@ -135,6 +135,51 @@ diagnostic system: - the ``METER`` macros and meter subsystem. +Statistics +.......... + +_`.stat`: The statistic system collects information about the +behaviour and performance of the MPS that may be useful for MPS +developers and customers, but which is not needed by the MPS itself +for internal decision-making. + +_`.stat.remove`: The space needed for these statistics, and the code +for maintaining them, can therefore be removed (compiled out) in some +varieties. + +_`.stat.config`: Statistics are compiled in if ``CONFIG_STATS`` is +defined (in the cool variety) and compiled out if +``CONFIG_STATS_NONE`` is defined (in the hot and rash varieties). + +``STATISTIC_DECL(decl)`` + +_`.stat.decl`: The ``STATISTIC_DECL`` macro is used to wrap the +declaration of storage for a statistic. Note that the expansion +supplies a terminating semi-colon and so it must not be followed by a +semi-colon in use. This is so that it can be used in structure +declarations. + +``STATISTIC(gather)`` + +_`.stat.gather`: The ``STATISTIC`` macro is used to gather statistics. +The argument is a statement and the expansion followed by a semicolon +is syntactically a statement. The macro expends to ``NOOP`` in +non-statistical varieties. (Note that it can't use ``DISCARD_STAT`` to +check the syntax of the statement because it is expected to use fields +that have been compiled away by ``STATISTIC_DECL``, and these will +cause compilation errors.) + +_`.stat.gather.effect`: The argument to the ``STATISTIC`` macro is not +executed in non-statistical varieties and must have no side effects, +except for updates to fields that are declared in ``STATISTIC_DECL``, +and telemetry output containing the values of such fields. + +``STATISTIC_WRITE(format, arg)`` + +_`.stat.write`: The ``STATISTIC_WRITE`` macro is used in ``WriteF()`` +argument lists to output the values of statistics. + + Related systems ............... @@ -193,8 +238,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2013-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 diff --git a/mps/design/exec-env.txt b/mps/design/exec-env.txt index 65de1dd228f..d1199c4e14d 100644 --- a/mps/design/exec-env.txt +++ b/mps/design/exec-env.txt @@ -78,9 +78,9 @@ 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`_). +environment, and therefore we may not call ``abort()``. We can't call +``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 @@ -112,10 +112,10 @@ 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. +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 @@ -149,8 +149,8 @@ Document History Copyright and License --------------------- -Copyright © 1996-2015 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 1996-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 diff --git a/mps/design/failover.txt b/mps/design/failover.txt index cdb7cf3a613..4cf010c766a 100644 --- a/mps/design/failover.txt +++ b/mps/design/failover.txt @@ -46,31 +46,27 @@ abstract data type, so the interface consists of the generic functions for lands. See design.mps.land_. -External types -.............. +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()``. +``FailoverStruct`` is typically embedded in another structure. -External functions -.................. +Classes +....... -``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()``. +_`.class`: ``CLASS(Failover)`` is the fail-over allocator class, a +subclass of ``CLASS(Land)`` suitable for passing to ``LandInit()``. Keyword arguments ................. -When initializing a fail-over allocator, ``LandCreate()`` and -``LandInit()`` require these two keyword arguments: +When initializing a fail-over allocator, ``LandInit()`` requires these +two keyword arguments: * ``FailoverPrimary`` (type ``Land``) is the primary land. diff --git a/mps/design/finalize.txt b/mps/design/finalize.txt index e3668716df7..65f82562494 100644 --- a/mps/design/finalize.txt +++ b/mps/design/finalize.txt @@ -6,7 +6,7 @@ Finalization :Tag: design.mps.finalize :Author: David Jones :Date: 1997-02-14 -:Status: incomplete design +:Status: complete design :Revision: $Id$ :Copyright: See `Copyright and License`_. :Index terms: pair: finalization; design @@ -15,68 +15,93 @@ Finalization Overview -------- -_`.overview`: Finalization is implemented internally using the -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. +_`.overview`: Finalization is implemented internally using the MRG +pool class (design.mps.poolmrg_). Objects can be registered for +finalization by calling ``mps_finalize()``. Notification of +finalization is given to the client via the messaging interface +(design.mps.message_). The MRG pool class implements a ``Message`` +subclass which implements the finalization messages. .. _design.mps.poolmrg: poolmrg +.. _design.mps.message: message Requirements ------------ _`.req`: Historically only Dylan had requirements for finalization, -see req.dylan.fun.final. Now (2003-02-19) Configura have requirements +see req.dylan.fun.final_. Now (2003-02-19) Configura have requirements for finalization. Happily they are very similar. - -Architecture ------------- - -External interface -.................. - -_`.if.register`: ``mps_finalize()`` increases the number of times that -an object has been registered for finalization by one. The object must -have been allocated from the arena (space). Any finalization messages -that are created for this object will appear on the arena's message -queue. The MPS will attempt to finalize the object that number of -times. - -_`.if.deregister`: ``mps_definalize()`` reduces the number of times that -the object located at ``obj`` has been registered for finalization by -one. It is an error to definalize an object that has not been -registered for finalization. - -_`.if.deregister.not`: At the moment (1997-08-20) ``mps_definalize()`` -is not implemented. - -_`.if.get-ref`: ``mps_message_finalization_ref()`` returns the reference -to the finalized object stored in the finalization message. +.. _req.dylan.fun.final: https://info.ravenbrook.com/project/mps/import/2001-09-27/mminfo/doc/req/dylan Implementation -------------- -_`.int.over`: Registering an object for finalization corresponds to +_`.impl.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_). +allocated in a guardian object in a pool belonging to the MRG pool +class (see 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". +_`.impl.arena.struct`: A single pool belonging to the MRG pool class +and used for managing final references is kept in the arena and +referred to as the "final pool". -_`.int.arena.lazy`: The pool is lazily created. It will not be created -until the first object is registered for finalization. +_`.impl.arena.lazy`: The final pool is lazily created. It is not +created until the first object is registered for finalization. -_`.int.arena.flag`: There is a flag in the Arena that indicates +_`.impl.arena.flag`: There is a flag in the Arena that indicates whether the final pool has been created yet or not. +_`.impl.scan`: An object is determined to be finalizable if it is +fixed at rank FINAL for a trace, and was not fixed at any lower rank +for that trace. See design.mps.poolmrg.scan.wasold_. + +.. _design.mps.poolmrg.scan.wasold: poolmrg#scan-wasold + +_`.impl.message`: When an object is determined to be finalizable, a +message for that object is posted to the arena's message queue. + +_`.impl.arena-destroy.empty`: ``ArenaDestroy()`` empties the message +queue by calling ``MessageEmpty()``. + +_`.impl.arena-destroy.final-pool`: If the final pool has been created +then ``ArenaDestroy()`` destroys the final pool. + +_`.impl.access`: ``mps_message_finalization_ref()`` needs to access +the finalization message to retrieve the reference and then write it +to where the client asks. This must be done carefully, in order to +avoid invalidating collection invariants such as the segment summary. + +_`.impl.invariants`: We protect the invariants by using +``ArenaRead()`` and ``ArenaWrite()`` to read and write the reference +via the software barrier. + + +External interface +------------------ + +_`.if.register`: ``mps_finalize()`` registers an object for +finalization. + +_`.if.deregister`: ``mps_definalize()`` deregisters an object for +finalization. It is an error to definalize an object that has not been +registered for finalization. + +_`.if.get-ref`: ``mps_message_finalization_ref()`` returns the reference +to the finalized object stored in the finalization message. + +_`.if.multiple`: The external interface allows an object to be +registered multiple times, but does not specify the number of +finalization messages that will be posted for that object. + + +Internal interface +------------------ + ``Res ArenaFinalize(Arena arena, Ref addr)`` _`.int.finalize.create`: Creates the final pool if it has not been @@ -84,43 +109,36 @@ created yet. _`.int.finalize.alloc`: Allocates a guardian in the final pool. +_`.int.finalize.alloc.multiple`: A consequence of this implementation +is that if an object is finalized multiple times, then multiple +guardians are created in the final pool, and so multiple messages will +be posted to the message queue when the object is determined to be +finalizable. But this behaviour is not guaranteed by the +documentation, leaving us free to change the iplementation. + _`.int.finalize.write`: Writes a reference to the object into the guardian object. _`.int.finalize.all`: That's all. _`.int.finalize.error`: If either the creation of the pool or the -allocation of the object fails then the error will be reported back to -the caller. +allocation of the object fails then the error is returned to the +caller. _`.int.finalize.error.no-unwind`: This function does not need to do any unwinding in the error cases because the creation of the pool is not something that needs to be undone. -_`.int.arena-destroy.empty`: ``ArenaDestroy()`` empties the message -queue by calling ``MessageEmpty()``. +``Res ArenaDefinalize(Arena arena, Ref obj)`` -_`.int.arena-destroy.final-pool`: If the final pool has been created -then ``ArenaDestroy()`` destroys the final pool. +_`.int.definalize.fail`: If the final pool has not been created, +return ``ResFAIL`` immediately. -_`.access`: ``mps_message_finalization_ref()`` needs to access the -finalization message to retrieve the reference and then write it to -where the client asks. This must be done carefully, in order to avoid -breaking the invariants or creating a hidden root. - -_`.access.invariants`: We protect the invariants by using special -routines ``ArenaRead()`` and ``ArenaPoke()`` to read and write the -reference. This works as long as there's no write-barrier collection. - -.. note:: - - Instead of ``ArenaPoke()``, we could put in an ``ArenaWrite()`` - that would be identical to ``ArenaPoke()``, except that it would - ``AVER()`` the invariant (or it can just ``AVER()`` that there are - no busy traces unflipped). When we get write-barrier collection, - we could change it to do the real thing, but in the absence of a - write-barrier, it's functionally identical to ``ArenaPoke()``. - Pekka P. Pirinen, 1997-12-09. +_`.int.definalize.search`: Otherwise, search for a guardian in the +final pool that refers to the object and which has not yet been +finalized. If one is found, delete it and return ``ResOK``. Otherwise +no guardians in the final pool refer to the object, so return +``ResFAIL``. Document History @@ -139,8 +157,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2013-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 diff --git a/mps/design/fix.txt b/mps/design/fix.txt index dce6b561c84..da7adf9b64c 100644 --- a/mps/design/fix.txt +++ b/mps/design/fix.txt @@ -21,26 +21,23 @@ interface also allows the value of such references to be changed (this is necessary in order to implement a moving memory manager). -Architecture -------------- +Was-marked protocol +------------------- -_`.protocol.was-marked`: The ``ScanState`` has a ``Bool`` -``wasMarked`` field. This is used for finalization. +_`.was-marked`: The ``ScanState`` has a ``Bool wasMarked`` +field. This is used for finalization. -_`.protocol.was-marked.set`: All pool-specific fix methods must set -the ``wasMarked`` field in the ``ScanState`` that they are passed. +_`.was-marked.not`: If a segment's fix method discovers that the +object referred to by the ref (the one that it is supposed to be +fixing) has not previously been marked (that is, this is the first +reference to this object that has been fixed), and that the object was +white (that is, in condemned space), it should (but need not) set the +field to ``FALSE`` in the passed ``ScanState````wasMarked`` . -_`.protocol.was-marked.meaning`: If the pool-specific fix method sets -the ``wasMarked`` field to ``FALSE`` it is indicating the object -referred to by the ref (the one that it is supposed to be fixing) has -not previously been marked (ie, this is the first reference to this -object that has been fixed), and that the object was white (in -condemned space). +_`.was-marked.otherwise`: Otherwise, the fix method must +leave the ``wasMarked`` field unchanged. -_`.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_) +_`.was-marked.finalizable`: The MRG pool (design.mps.poolmrg_) uses the value of the ``wasMarked`` field to determine whether an object is finalizable. @@ -66,6 +63,8 @@ Document History - 2013-04-14 GDR_ Converted to reStructuredText. +- 2018-06-18 GDR_ Simplify the ``wasMarked`` protocol. + .. _RB: http://www.ravenbrook.com/consultants/rb/ .. _GDR: http://www.ravenbrook.com/consultants/gdr/ @@ -73,8 +72,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2013-2018 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 diff --git a/mps/design/freelist.txt b/mps/design/freelist.txt index c89181505c1..3907e43bb1d 100644 --- a/mps/design/freelist.txt +++ b/mps/design/freelist.txt @@ -67,26 +67,22 @@ Types ``typedef struct FreelistStruct *Freelist`` -_`.type.freelist`: The type of free lists. A ``FreelistStruct`` may be -embedded in another structure, or you can create it using -``LandCreate()``. +_`.type.freelist`: The type of free lists. A ``FreelistStruct`` is +typically embedded in another structure. -External functions -.................. +Classes +....... -``LandClass FreelistLandClassGet(void)`` - -_`.function.class`: The function ``FreelistLandClassGet()`` returns -the free list class, a subclass of ``LandClass`` suitable for passing -to ``LandCreate()`` or ``LandInit()``. +_`.class`: ``CLASS(Freelist)`` is the free list class, a subclass of +``CLASS(Land)`` suitable for passing to ``LandInit()``. Keyword arguments ................. -When initializing a free list, ``LandCreate()`` and ``LandInit()`` -take no keyword arguments. Pass ``mps_args_none``. +When initializing a free list, ``LandInit()`` takes no keyword +arguments. Pass ``mps_args_none``. Implementation @@ -134,7 +130,7 @@ _`.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 +.. _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, @@ -172,8 +168,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2013-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 diff --git a/mps/design/guide.hex.trans.txt b/mps/design/guide.hex.trans.txt index 8120f6e53e5..bf20d099096 100644 --- a/mps/design/guide.hex.trans.txt +++ b/mps/design/guide.hex.trans.txt @@ -144,8 +144,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +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 diff --git a/mps/design/guide.impl.c.format.txt b/mps/design/guide.impl.c.format.txt index 4f827bc489e..a71f264edf2 100644 --- a/mps/design/guide.impl.c.format.txt +++ b/mps/design/guide.impl.c.format.txt @@ -25,10 +25,10 @@ _`.readership`: This document is intended for anyone working on or with the C source code. -General Formatting Conventions +General formatting conventions ------------------------------ -Line Width +Line width .......... _`.width`: Lines should be no wider than 72 characters. _`.width.why`: Many @@ -37,11 +37,12 @@ placed side by side. Restricting lines to 72 characters allows line numbering to be used (in vi for example) and also allows diffs to be displayed without overflowing the terminal. -White Space +White space ........... _`.space.notab`: No tab characters should appear in the source files. Ordinary spaces should be used to indent and format the sources. + _`.space.notab.why`: Tab characters are displayed differently on different platforms, and sometimes translated back and forth, destroying layout information. @@ -55,15 +56,18 @@ sub-expressions more tightly. For example:: foo = x + y*z; -_`.space.control`: One space between the keyword, ``switch``, ``while``, -``for`` and the following paren. _`.space.control.why`: This distinguishes -control statements lexically from function calls, making it easier to -distinguish them visually and when searching with tools like grep. +_`.space.control`: One space between a control-flow keyword +(``switch``, ``while``, ``for``, ``if``) and the following opening +parenthesis. -_`.space.function.not`: No space between a function name and the following -paren beginning its argument list. +_`.space.control.why`: This distinguishes control statements lexically +from function calls, making it easier to distinguish them visually and +when searching with tools like ``grep``. -Sections and Paragraphs +_`.space.function.not`: No space between a function name and the opening +parenthesis beginning its argument list. + +Sections and paragraphs ....................... _`.section`: Source files can be thought of as breaking down into @@ -74,7 +78,7 @@ _`.section.space`: Precede sections by two blank lines (except the first one in the file, which should be the leader comment in any case). _`.section.comment`: Each section should start with a banner comment (see -.comment.banner) describing what the section contains. +`.comment.banner`_) describing what the section contains. _`.para`: Within sections, code often breaks down into natural units called "paragraphs". A paragraph might be a set of strongly related @@ -149,9 +153,7 @@ Some examples:: _`.indent.goto-label`: Place each goto-label on a line of its own, outdented to the same level as the surrounding block. Then indent the -non-label part of the statement normally. - -:: +non-label part of the statement normally. :: result foo(void) { @@ -177,22 +179,22 @@ _`.indent.cont.parens`: if you break a statement inside a parameter list or other parenthesized expression, indent so that the continuation lines up just after the open parenthesis. For example:: - PoolClassInit(&PoolClassMVStruct, - "MV", init, finish, allocP, freeP, - NULL, NULL, describe, isValid); + res = ChunkInit(chunk, arena, alignedBase, + AddrAlignDown(limit, ArenaGrainSize(arena)), + AddrOffset(base, limit), boot); _`.indent.cont.expr`: Note that when breaking an expression it is clearer to place the operator at the start of the continuation line:: - CHECKL(AddrAdd((Addr)chunk->pageTableMapped, - BTSize(chunk->pageTablePages)) - <= AddrAdd(chunk->base, chunk->ullageSize)); + CHECKL(AddrAdd((Addr)chunk->allocTable, BTSize(chunk->pages)) + <= PageIndexBase(chunk, chunk->allocBase)); This is particularly useful in long conditional expressions that use && and ||. For example:: - } while(trace->state != TraceFINISHED - && (trace->emergency || traceWorkClock(trace) < pollEnd)); + if (BufferRankSet(buffer) != RankSetEMPTY + && (buffer->mode & BufferModeFLIPPED) == 0 + && !BufferIsReset(buffer)) _`.indent.hint`: Usually, it is possible to determine the correct indentation for a line by looking to see if the previous line ends with @@ -200,7 +202,7 @@ a semicolon. If it does, indent to the same amount, otherwise indent by two more spaces. The main exceptions are lines starting with a close brace, goto-labels, and line-breaks between parentheses. -Positioning of Braces +Positioning of braces ..................... _`.brace.otb`: Use the "One True Brace" (or OTB) style. This places the @@ -208,21 +210,13 @@ open brace after the control word or expression, separated by a space, and when there is an else, places that after the close brace. For example:: - if(isBase) { - new->base = limit; - new->limit = block->limit; - block->limit = base; - new->next = block->next; - block->next = new; + if (buffer->mode & BufferModeFLIPPED) { + return buffer->initAtFlip; } else { - new->base = block->base; - new->limit = base; - block->base = limit; - new->next = block; - *prev = new; + return buffer->ap_s.init; } -The same applies to struct, enum, union. +The same applies to ``struct``, ``enum``, and ``union``. _`.brace.otb.function.not`: OTB is never used for function definitions. @@ -239,54 +233,46 @@ this will be a ``goto`` or an assignment. For example:: Note in particular that an ``if`` with an ``else`` must have braces on both paths. -Switch Statements +Switch statements ................. _`.switch`: format switch statements like this:: - switch (action) { - case WIBBLE: - case WOBBLE: - { - int angle; - err = move(plate, action, &angle); - } - break; - - case QUIET: - drop(); - /* fall-through */ - - case QUIESCENT: - err = 0; - break; - + switch (SplaySplay(splay, oldKey, splay->compare)) { default: NOTREACHED; - break; + /* fall through */ + case CompareLESS: + return SplayTreeRoot(splay); + + case CompareGREATER: + case CompareEQUAL: + return SplayTreeSuccessor(splay); } The component rules that result in this style are: _`.switch.break`: The last line of every case-clause body must be an unconditional jump statement (usually ``break``, but may be ``goto``, -``continue``, or ``return``), or if a fall-through is intended, the comment -``/* fall-through */``. (Note: if the unconditional jump should never be -taken, because of previous conditional jumps, use ``NOTREACHED`` on the -line before it). This rule is to prevent accidental fall-throughs, even -if someone makes a editing mistake that causes a conditional jump to be -missed. +``continue``, or ``return``), or if a fall-through is intended, the +comment ``/* fall through */``. (Note: if the unconditional jump +should never be taken, because of previous conditional jumps, use +``NOTREACHED`` on the line before it.) This rule is to prevent +accidental fall-throughs, even if someone makes a editing mistake that +causes a conditional jump to be missed. This rule is automatically +checked by GCC and Clang with the ``-Wimplicit-fallthrough`` option. -_`.switch.default`: It is usually a good idea to have a default-clause, -even if all it contains is ``NOTREACHED`` and ``break``. Remember that -``NOTREACHED`` doesn't stop the process in all build varieties. +_`.switch.default`: It is usually a good idea to have a +default-clause, even if all it contains is ``NOTREACHED`` and +``break`` or ``/* fall through */``. Remember that ``NOTREACHED`` +doesn't stop the process in all build varieties. -Formatting Comments -................... +Comments +........ _`.comment`: There are three types of comments: banners, paragraph -comments, and columnar comments. +comments, and column comments. _`.comment.banner`: Banner comments come at the start of sections. A banner comment consists of a heading usually composed of a symbol, an em-dash @@ -307,19 +293,18 @@ the banner comment and the section it comments. For example:: typedef struct BlockStruct { -_`.comment.para`: Paragraph comments come at the start of paragraphs in the -code. A paragraph comment consists of formatted English text, with each -line wrapped by the open and close comment tokens (``/*`` and ``*/``). -(This avoids problems when cutting and pasting comments.) For example:: +_`.comment.para`: Paragraph comments come at the start of paragraphs +in the code. A paragraph comment consists of formatted English text. +For example:: - /* If the freed area is in the base sentinel then insert */ - /* the new descriptor after it, otherwise insert before. */ - if(isBase) { + /* If the freed area is in the base sentinel then insert + the new descriptor after it, otherwise insert before. */ + if (isBase) { _`.comment.para.precede`: Paragraph comments, even one-liners, precede the code to which they apply. -_`.comment.column`: Columnar comments appear in a column to the right of +_`.comment.column`: Column comments appear in a column to the right of the code. They should be used sparingly, since they clutter the code and make it hard to edit. Use them on variable declarations and structure, union, or enum declarations. They should start at least at column 32 @@ -327,16 +312,22 @@ union, or enum declarations. They should start at least at column 32 descriptive text. Abandon English sentence structure if this makes the comment clearer. Don't write more than one line. Here's an example:: - typedef struct PoolMVStruct { - Pool blockPool; /* for block descriptors */ - Pool spanPool; /* for span descriptors */ - size_t extendBy; /* size to extend pool by */ - size_t avgSize; /* estimate of allocation size */ - size_t maxSize; /* estimate of maximum size */ - Addr space; /* total free space in pool */ - Addr lost; /* lost when free can't allocate */ - struct SpanStruct *spans; /* span chain */ - } PoolMVStruct; + typedef struct MVFFStruct { /* MVFF pool outer structure */ + PoolStruct poolStruct; /* generic structure */ + LocusPrefStruct locusPrefStruct; /* the preferences for allocation */ + Size extendBy; /* size to extend pool by */ + Size avgSize; /* client estimate of allocation size */ + 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; + Macros ...... diff --git a/mps/design/guide.review.txt b/mps/design/guide.review.txt index b1298b26b90..52d8f4635ea 100644 --- a/mps/design/guide.review.txt +++ b/mps/design/guide.review.txt @@ -56,8 +56,8 @@ Document History Copyright and License --------------------- -Copyright © 2015 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +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 diff --git a/mps/design/index.txt b/mps/design/index.txt index 9e0a1090cda..2712128a2cc 100644 --- a/mps/design/index.txt +++ b/mps/design/index.txt @@ -49,7 +49,6 @@ bt_ Bit tables buffer_ Allocation buffers and allocation points cbs_ Coalescing block structures check_ Checking -class-interface_ Pool class interface clock_ Fast high-resolution clock collection_ Collection framework config_ MPS configuration @@ -75,24 +74,21 @@ 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 +pool_ Pool classes 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 +prmc_ Mutator context prot_ Memory protection -protli_ Linux implementation of protection module +protix_ POSIX implementation of protection module protocol_ Protocol inheritance -protsu_ SunOS 4 implementation of protection module pthreadext_ POSIX thread extensions range_ Ranges of addresses -reservoir_ The low-memory reservoir ring_ Ring data structure root_ Root manager scan_ The generic scanner @@ -101,8 +97,7 @@ shield_ Shield sig_ Signatures in the MPS sp_ Stack probe splay_ Splay trees -ss_ Stack and register scanning -sso1al_ Stack scanner for Digital Unix / Alpha systems +stack-scan_ Stack and register scanning strategy_ Collection strategy telemetry_ Telemetry tests_ Tests @@ -112,10 +107,8 @@ thread-safety_ Thread safety in the MPS trace_ Tracer 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 +write-barrier_ Write Barrier writef_ The WriteF function ====================== ================================================ @@ -130,7 +123,6 @@ writef_ The WriteF function .. _cbs: cbs .. _check: check .. _clock: clock -.. _class-interface: class-interface .. _collection: collection .. _config: config .. _critical-path: critical-path @@ -162,17 +154,14 @@ writef_ The WriteF function .. _poollo: poollo .. _poolmfs: poolmfs .. _poolmrg: poolmrg -.. _poolmv: poolmv .. _poolmvt: poolmvt .. _poolmvff: poolmvff .. _prmc: prmc .. _prot: prot -.. _protli: protli +.. _protix: protix .. _protocol: protocol -.. _protsu: protsu .. _pthreadext: pthreadext .. _range: range -.. _reservoir: reservoir .. _ring: ring .. _root: root .. _scan: scan @@ -181,8 +170,7 @@ writef_ The WriteF function .. _sig: sig .. _sp: sp .. _splay: splay -.. _ss: ss -.. _sso1al: sso1al +.. _stack-scan: stack-scan .. _strategy: strategy .. _telemetry: telemetry .. _tests: tests @@ -192,10 +180,8 @@ writef_ The WriteF function .. _trace: trace .. _type: type .. _version-library: version-library -.. _version: version .. _vm: vm -.. _vmo1: vmo1 -.. _vmso: vmso +.. _write-barrier: write-barrier .. _writef: writef @@ -213,23 +199,25 @@ References Document History ---------------- -- 2002-05-23 RB_ Created empty catalogue based on P4DTI design document catalogue. -- 2002-06-07 RB_ Added a bunch of design documents referenced by the source code. -- 2002-06-21 NB_ Remove P4DTI reference, which doesn't fit here. Maybe one day we'll have a corporate design document procedure. -- 2002-06-24 RB_ Added fix, object-debug, thread-manager, and thread-safety. -- 2007-02-08 RHSK Added message-gc and shield. -- 2007-06-12 RHSK Added cstyle. -- 2007-06-28 RHSK Added diag. -- 2008-12-04 RHSK Added tests. -- 2008-12-10 RHSK Correct description of message-gc: gc begin or end. -- 2012-09-14 RB_ Added link to critical-path -- 2013-05-10 RB_ Fixed link to sig and added guide.hex.trans -- 2013-05-22 GDR_ Add link to keyword-arguments. -- 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]_. +- 2002-05-23 RB_ Created empty catalogue based on P4DTI design document catalogue. +- 2002-06-07 RB_ Added a bunch of design documents referenced by the source code. +- 2002-06-21 NB_ Remove P4DTI reference, which doesn't fit here. Maybe one day we'll have a corporate design document procedure. +- 2002-06-24 RB_ Added fix, object-debug, thread-manager, and thread-safety. +- 2007-02-08 RHSK Added message-gc and shield. +- 2007-06-12 RHSK Added cstyle. +- 2007-06-28 RHSK Added diag. +- 2008-12-04 RHSK Added tests. +- 2008-12-10 RHSK Correct description of message-gc: gc begin or end. +- 2012-09-14 RB_ Added link to critical-path +- 2013-05-10 RB_ Fixed link to sig and added guide.hex.trans +- 2013-05-22 GDR_ Add link to keyword-arguments. +- 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. - +- 2016-03-22 RB_ Add write-barier. +- 2016-03-27 RB_ Goodbye pool MV *sniff*. + .. _RB: http://www.ravenbrook.com/consultants/rb .. _NB: http://www.ravenbrook.com/consultants/nb .. _GDR: http://www.ravenbrook.com/consultants/gdr @@ -239,8 +227,8 @@ Document History Copyright and License --------------------- -Copyright © 2002-2016 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2002-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 diff --git a/mps/design/interface-c.txt b/mps/design/interface-c.txt index 1e5424105b7..912b4aa9bdf 100644 --- a/mps/design/interface-c.txt +++ b/mps/design/interface-c.txt @@ -405,8 +405,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +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 diff --git a/mps/design/io.txt b/mps/design/io.txt index 27ab9d7802a..0a89d5e6d41 100644 --- a/mps/design/io.txt +++ b/mps/design/io.txt @@ -431,8 +431,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2015 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2013-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 diff --git a/mps/design/keyword-arguments.txt b/mps/design/keyword-arguments.txt index af6db232a5a..5a7f1d1f1a5 100644 --- a/mps/design/keyword-arguments.txt +++ b/mps/design/keyword-arguments.txt @@ -163,8 +163,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +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 diff --git a/mps/design/land.txt b/mps/design/land.txt index 15c1454882d..79872368b5b 100644 --- a/mps/design/land.txt +++ b/mps/design/land.txt @@ -78,14 +78,14 @@ Types _`.type.land`: The type of a generic land instance. -``typedef Bool (*LandVisitor)(Land land, Range range, void *closureP, Size closureS)`` +``typedef Bool (*LandVisitor)(Land land, Range range, void *closure)`` _`.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)`` +``typedef Bool (*LandDeleteVisitor)(Bool *deleteReturn, Land land, Range range, void *closure)`` _`.type.deletevisitor`: Type ``LandDeleteVisitor`` is a callback function that may be passed to ``LandIterateAndDelete()``. It is called for every isolated @@ -115,11 +115,6 @@ _`.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 @@ -172,17 +167,17 @@ strategy. _`.function.delete.alias`: It is acceptable for ``rangeReturn`` and ``range`` to share storage. -``Bool LandIterate(Land land, LandVisitor visitor, void *closureP, Size closureS)`` +``Bool LandIterate(Land land, LandVisitor visitor, void *closure)`` _`.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 +visitor function to invoke on every range, and a closure pointer +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)`` +``Bool LandIterateAndDelete(Land land, LandDeleteVisitor visitor, void *closure)`` _`.function.iterate.and.delete`: As ``LandIterate()``, but the visitor function additionally returns a Boolean indicating whether the range diff --git a/mps/design/lib.txt b/mps/design/lib.txt index 1dd7efe6964..1da7f673c49 100644 --- a/mps/design/lib.txt +++ b/mps/design/lib.txt @@ -95,8 +95,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2015 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2013-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 diff --git a/mps/design/lock.txt b/mps/design/lock.txt index 069474ae428..9d390a1a8bd 100644 --- a/mps/design/lock.txt +++ b/mps/design/lock.txt @@ -59,28 +59,35 @@ be claimed again by the thread currently holding them, without blocking or deadlocking. (This is needed to implement the global recursive lock.) +_`.req.held`: Provide a means to test if a lock is held. (This is +needed for debugging a dynamic function table callback on Windows on +x86-64. See ``mps_arena_busy()`` for a detailed description of this +use case. Note that in this use case the program is running +single-threaded and so there is no need for this feature to be +thread-safe.) + _`.req.global`: Provide *global* locks: that is locks that need not be allocated or initialized by the user. _`.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_.) +design.mps.thread-safety.sol.global.mutable_.) -.. _design.mps.thread-safety.arch.global.binary: thread-safety#arch.global.binary +.. _design.mps.thread-safety.sol.global.mutable: thread-safety#sol-global-mutable _`.req.global.recursive`: Provide a global recursive lock. (This is -required to protect pool class initialization: see -design.mps.thread-safety.arch.global.recursive_.) +required to protect protocol class initialization: see +design.mps.thread-safety.sol.global.once_.) -.. _design.mps.thread-safety.arch.global.recursive: thread-safety#arch.global.recursive +.. _design.mps.thread-safety.sol.global.once: thread-safety#sol-global-once _`.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.sol.deadlock_.) -.. _design.mps.thread-safety.deadlock: thread-safety#deadlock +.. _design.mps.thread-safety.sol.deadlock: thread-safety#sol-deadlock Interface @@ -124,6 +131,20 @@ thread and claims the lock (if not already held). Restores the previous state of the lock remembered by the corresponding ``LockClaimRecursive()`` call. +``Bool LockIsHeld(Lock lock)`` + +Return true if the lock is held by any thread, false otherwise. Note +that this function need not be thread-safe (see `.req.held`_). + +``void LockInitGlobal(void)`` + +Initialize (or re-initialize) the global locks. This should only be +called in the following circumstances: the first time either of the +global locks is claimed; and in the child process after a ``fork()``. +See design.mps.thread-safety.sol.fork.lock_. + +.. _design.mps.thread-safety.sol.fork.lock: thread-safety#sol-fork-lock + ``void LockClaimGlobal(void)`` Claims ownership of the binary global lock which was previously not @@ -143,6 +164,11 @@ to the current thread and claims the lock (if not already held). Restores the previous state of the recursive global lock remembered by the corresponding ``LockClaimGlobalRecursive()`` call. +``void LockSetup(void)`` + +One-time initialization function, intended for calling +``pthread_atfork()`` on the appropriate platforms: see design.mps.thread-safety.sol.fork.lock_. + Implementation -------------- @@ -194,15 +220,6 @@ _`.impl.ix`: POSIX implementation ``lockix.c``: 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 ------- @@ -258,11 +275,6 @@ References "Critical Section Objects"; -.. [LinuxThreads] - Xavier Leroy; - "The LinuxThreads library"; - - .. [POSIXThreads] The Open Group; "The Single UNIX Specification, Version 2---Threads"; @@ -280,6 +292,8 @@ Document History - 2014-10-21 GDR_ Brought up to date. +- 2018-06-14 GDR_ Added ``LockInitGlobal()``. + .. _RB: http://www.ravenbrook.com/consultants/rb/ .. _GDR: http://www.ravenbrook.com/consultants/gdr/ @@ -287,8 +301,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2013-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 diff --git a/mps/design/locus.txt b/mps/design/locus.txt index 4777857c9d7..ebee711f481 100644 --- a/mps/design/locus.txt +++ b/mps/design/locus.txt @@ -700,8 +700,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +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 diff --git a/mps/design/message-gc.txt b/mps/design/message-gc.txt index f69a3d2c3f0..94de3049552 100644 --- a/mps/design/message-gc.txt +++ b/mps/design/message-gc.txt @@ -317,8 +317,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +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 diff --git a/mps/design/message.txt b/mps/design/message.txt index 807f9cc6cdd..a30686720ea 100644 --- a/mps/design/message.txt +++ b/mps/design/message.txt @@ -6,7 +6,7 @@ Client message protocol :Tag: design.mps.message :Author: David Jones :Date: 1997-02-13 -:Status: incomplete document +:Status: complete document :Revision: $Id$ :Copyright: See `Copyright and License`_. :Index terms: @@ -18,24 +18,16 @@ Introduction ------------ _`.intro`: The client message protocol provides a means by which -clients can receive messages from the MPS asynchronously. Typical -messages may be low memory notification (or in general low utility), -finalization notification, soft-failure notification. There is a -general assumption that it should not be disastrous for the MPS client -to ignore messages, but that it is probably in the clients best -interest to not ignore messages. The justification for this is that -the MPS cannot force the MPS client to read and act on messages, so no -message should be critical. +clients can receive messages from the MPS. The motivating use case is +finalization notification (see design.mps.finalize_), but the +mechanism is also used for feedback about collections. -.. note:: - - Bogus, since we cannot force clients to check error codes either. - Pekka P. Pirinen, 1997-09-17. +.. _design.mps.finalize: finalize _`.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 the MRG pool. _`.readership`: Any MPS developer. @@ -43,21 +35,58 @@ _`.readership`: Any MPS developer. Requirements ------------ -_`.req`: The client message protocol will be used for implementing -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). +_`.req.synchronous`: The message protocol must be synchronous with the +client program: that is, the client program must be able to choose +when to collect and act on messages. Justification: [Boehm_2002]_ +shows that asynchronous finalization is impossible to implement +correctly. -.. _design.mps.finalize: finalize +_`.req.reliable`: Posting a message must be reliable: that is, it must +not fail for a dynamic reason such as running out memory to store the +message. Justification: messages can't be used to implement +finalization unless the messages can be delivered reliably. + +_`.req.extensible.types`: The message mechanism must be extensible +with new types of message in future versions of the MPS, without +breaking client programs that do not receive those types of message. + +_`.req.resources`: It follows from `.req.extensible.types`_ that +messages must not use resources unless the client program has +requested them (otherwise resources would leak in client programs that +have not been updated to handle new types of message). + +_`.req.extensible.fields`: It must be possible to add new fields to +existing types of message in future versions of the MPS, without +breaking client programs that do not receive those types of message. + + +Design +------ + +_`.sol.synchronous`: Messages are stored on a ring belonging to the +arena. An interface is provided that allows the client program to +collect messages from the ring at a time of its choosing. + +_`.sol.reliable`: The memory needed for the message is allocated at an +earlier point in time, when it possible to communicate an allocation +failure via a result code. In particular, space for a finalization +message is allocated when the client program calls ``mps_finalize()``, +and space for trace messages is allocated in the arena (there can be +at most one instance of each message per trace, and the maximum number +of traces is known statically). + +_`.sol.resources`: Messages are not posted unless they belong to a +type that has been enabled by the client program calling +``mps_message_enable()``. This means that message types that are not +understood by the client program are not posted and use no resources. + +_`.sol.extensible.fields`: Message fields are retrieved by calling +accessor functions. External interface ------------------ -_`.if.queue`: Messages are presented as a single queue per arena. -Various functions are provided to inspect the queue and inspect -messages in it (see below). - Functions ......... @@ -73,12 +102,7 @@ messages of a certain type. The queue of messages of a arena will contain only messages whose types have been enabled. Initially all message types are disabled. Effectively this function allows the client to declare to the MPS what message types the client -understands. The MPS does not generate any messages of a type that -hasn't been enabled. This allows the MPS to add new message types (in -subsequent releases of a memory manager) without confusing the client. -The client will only be receiving the messages if they have explicitly -enabled them (and the client presumably only enables message types -when they have written the code to handle them). +understands. _`.if.fun.disable`: ``mps_message_type_disable()`` disables the flow of messages of a certain type. The antidote to @@ -122,10 +146,10 @@ Types of messages _`.type`: The type governs the "shape" and meaning of the message. -_`.type.int`: Types themselves will just be a scalar quantity, an -integer. +_`.type.int`: A message type is an integer belonging to the +``MessageType`` enumeration. -_`.type.semantics`: A type indicates the semantics of the message. +_`.type.semantics`: A type indicates the semantics of the message. _`.type.semantics.interpret`: The semantics of a message are interpreted by the client by calling various accessor methods on the @@ -134,43 +158,23 @@ message. _`.type.accessor`: The type of a message governs which accessor methods are legal to apply to the message. -_`.type.example`: Some example types: - -_`.type.finalization`: There will be a finalization type. The type is -abstractly: ``FinalizationMessage(Ref)``. +_`.type.finalization`: There is a finalization type, +``MessageTypeFINALIZATION``. _`.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: poolmrg#def.final.object +.. _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 -finalizable) called ``mps_message_finalization_ref()``. +_`.type.finalization.ref`: The accessor function +``mps_message_finalization_ref()`` retrieves the reference to the +object which is finalizable. _`.type.finalization.ref.scan`: Note that the reference returned -should be stored in scanned memory. +must be stored in scanned memory. -Compatibility issues -.................... - -_`.compatibility`: The following issues affect future compatibility of -the interface: - -_`.compatibility.future.type-new`: Notice that message of a type that -the client doesn't understand are not placed on the queue, therefore -the MPS can introduce new types of message and existing client will -still function and will not leak resources. This has been achieved by -getting the client to declare the types that the client understands -(with ``mps_message_type_enable()``, `.if.fun.enable`_). - -_`.compatibility.future.type-extend`: The information available in a -message of a given type can be extended by providing more accessor -methods. Old clients won't get any of this information but that's -okay. - Internal interface ------------------ @@ -184,8 +188,6 @@ _`.message.type`: ``Message`` is the type of messages. _`.message.instance`: Messages are instances of Message Classes. -``typedef struct MessageStruct *MessageStruct`` - _`.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 @@ -201,14 +203,14 @@ that specific type of message. _`.message.struct`: The structure is declared as follows:: - struct MessageStruct { - Sig sig; - MessageType type; - MessageClass class; - RingStruct node; + typedef struct mps_message_s { + Sig sig; /* */ + Arena arena; /* owning arena */ + MessageClass klass; /* Message Class Structure */ + Clock postedClock; /* mps_clock() at post time, or 0 */ + RingStruct queueRing; /* Message queue ring */ } MessageStruct; - ``typedef struct MessageClassStruct *MessageClass`` _`.class`: A message class is an encapsulation of methods. It @@ -239,22 +241,26 @@ _`.class.methods.generic`: The generic methods are as follows: _`.class.methods.specific`: The type specific methods are: _`.class.methods.specific.finalization`: Specific to -``MessageTypeFinalization``: +``MessageTypeFINALIZATION``: * ``finalizationRef`` -- returns a reference to the finalizable object represented by this message. -_`.class.methods.specific.collectionstats`: Specific to ``MessageTypeCollectionStats``: +_`.class.methods.specific.gc`: Specific to ``MessageTypeGC``: -* ``collectionStatsLiveSize`` -- returns the number of bytes (of - objects) that were condemned but survived. +* ``gcLiveSize`` -- returns the number of bytes (of objects) that were + condemned by the trace but survived. -* ``collectionStatsCondemnedSize`` -- returns the number of bytes - condemned in the collection. +* ``gcCondemnedSize`` -- returns the number of bytes condemned by the + trace. -* ``collectionStatsNotCondemnedSize`` -- returns the the number of - bytes (of objects) that are subject to a GC policy (that is, - collectable) but were not condemned in the collection. +* ``gcNotCondemnedSize`` -- returns the the number of bytes (of + objects) that are collectable but were not condemned by the trace. + +_`.class.methods.specific.gcstart`: Specific to ``MessageTypeGCSTART``: + +* ``gcStartWhy`` -- returns an English-language description of the + reason why the trace was started. _`.class.sig.double`: The ``MessageClassStruct`` has a signature field at both ends. This is so that if the ``MessageClassStruct`` changes @@ -266,24 +272,28 @@ signature) unless the static initializers are changed as well. _`.class.struct`: The structure is declared as follows:: typedef struct MessageClassStruct { - Sig sig; /* design.mps.sig */ + Sig sig; /* */ const char *name; /* Human readable Class name */ + MessageType type; /* Message Type */ + /* generic methods */ MessageDeleteMethod delete; /* terminates a message */ - /* methods specific to MessageTypeFinalization */ - MessageFinalizationRefMethod finalizationRef; + /* methods specific to MessageTypeFINALIZATION */ + MessageFinalizationRefMethod finalizationRef; - /* methods specific to MessageTypeCollectionStats */ - MessageCollectionStatsLiveSizeMethod collectionStatsLiveSize; - MessageCollectionStatsCondemnedSizeMethod collectionStatsCondemnedSize; - MessageCollectionStatsNotCondemnedSizeMethod collectionStatsNotCondemnedSize; + /* methods specific to MessageTypeGC */ + MessageGCLiveSizeMethod gcLiveSize; + MessageGCCondemnedSizeMethod gcCondemnedSize; + MessageGCNotCondemnedSizeMethod gcNotCondemnedSize; - Sig endSig; /* design.mps.message.class.sig.double */ + /* methods specific to MessageTypeGCSTART */ + MessageGCStartWhyMethod gcStartWhy; + + Sig endSig; /* */ } MessageClassStruct; - _`.space.queue`: The arena structure is augmented with a structure for managing for queue of pending messages. This is a ring in the ``ArenaStruct``:: @@ -299,7 +309,7 @@ managing for queue of pending messages. This is a ring in the Functions ......... -``void MessageInit(Arena arena, Message message, MessageClass class)`` +``void MessageInit(Arena arena, Message message, MessageClass klass, MessageType type)`` _`.fun.init`: Initializes the ``MessageStruct`` pointed to by ``message``. The caller of this function is expected to manage the @@ -315,12 +325,15 @@ store for the ``MessageStruct``. _`.fun.post`: Places a message on the queue of an arena. -_`.fun.post.precondition`: Prior to calling the function, the node -field of the message must be a singleton. After the call to the -function the message will be available for MPS client to access. After -the call to the function the message fields must not be manipulated -except from the message's class's method functions (that is, you -mustn't poke about with the node field in particular). +_`.fun.post.precondition`: Prior to calling the function, the +``queueRing`` field of the message must be a singleton +(design.mps.ring.def.singleton_). After the call to the function the +message will be available for MPS client to access. After the call to +the function the message fields must not be manipulated except from +the message's class's method functions (that is, you mustn't poke +about with the ``queueRing`` field in particular). + +.. _design.mps.ring.def.singleton: ring#def-singleton ``void MessageEmpty(Arena arena)`` @@ -336,51 +349,35 @@ the future. Message life cycle ------------------ -_`.life`: A message will be allocated by a client of the message -module, it will be initialised by calling ``MessageInit()``. The -client will eventually post the message on the external queue (in fact -most clients will create a message and then immediately post it). The -message module may then apply any of the methods to the message. The -message module will eventually destroy the message by applying the -``delete`` method to it. +_`.life.alloc`: Space for the message structure is allocated at the +earliest point in time when the MPS knows that the message might be +needed. + +_`.life.init`: The message structure is initialized by calling +``MessageInit()``. + +_`.life.post`: The message is posted on the arena's message queue by +calling ``MessagePost()``. + +_`.life.get`: The client program retrieves the message by calling ``mps_message_get()``. + +_`.life.discard`: The client program indicates that it is finished +with the message by calling ``mps_message_discard()``. + +_`.life.reuse`: The MPS may reuse the message structure, in which case +the lifecycle continues from `.life.post`_. + +_`.life.delete`: When the MPS no longer needs the message structure, +its ``delete`` method is called. -Examples --------- +References +---------- -Finalization -............ - -.. note:: - - 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_). - -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 -(which is when the referred to object is registered for finalization). -This avoids allocating at the time when the message gets posted (which -might be a tricky, undesirable, or impossible, time to allocate). - -PoolMRG has two queues: the entry queue, and the exit queue. The entry -queue will use a ring; the exit queue of MRG will simply be the -external message queue. - -The ``delete`` method frees both the link part and the reference part -of the guardian. +.. [Boehm_2002] Hans-J. Boehm. 2002. "`Destructors, Finalizers, and + Synchronization + `_". HP + Labs technical report HPL-2002-335. Document History @@ -407,8 +404,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2013-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 diff --git a/mps/design/nailboard.txt b/mps/design/nailboard.txt index 2f88b5b57b7..cce46ac34d0 100644 --- a/mps/design/nailboard.txt +++ b/mps/design/nailboard.txt @@ -214,8 +214,8 @@ Document History Copyright and License --------------------- -Copyright © 2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +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 diff --git a/mps/design/object-debug.txt b/mps/design/object-debug.txt index df89b5cf21c..a803b16eb1d 100644 --- a/mps/design/object-debug.txt +++ b/mps/design/object-debug.txt @@ -291,17 +291,10 @@ protocol. This will be improved, when we figure out formatted pools -- they don't need tags for fenceposting. -_`.out-of-space`: If there's no room for tags, we will not dip into -the reservoir, just fail to allocate the tag. If the alloc call had a -reservoir permit, we let it succeed even without a tag, and just make -sure the free method will not complain if it can't find a tag. If the -call didn't have a reservoir permit, we free the block allocated for -the object and fail the allocation, so that the client gets a chance -to do whatever low-memory actions they might want to do. - -.. note:: - - Should this depend on whether there is anything in the reservoir? +_`.out-of-space`: If there's no room for tags, we just fail to +allocate the tag. We free the block allocated for the object and fail +the allocation, so that the client gets a chance to do whatever +low-memory actions they might want to do. This breaks the one-to-one relationship between tags and objects, so some checks cannot be made, but we do count the "lost" tags. @@ -434,8 +427,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +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 diff --git a/mps/design/pool.txt b/mps/design/pool.txt index 2ad6d6e64d7..ff32c1a1cc4 100644 --- a/mps/design/pool.txt +++ b/mps/design/pool.txt @@ -1,75 +1,221 @@ .. mode: -*- rst -*- -Pool and pool class mechanisms -============================== +Pool classes +============ :Tag: design.mps.pool :Author: Richard Brooksby -:Date: 1996-07-31 -:Status: incomplete document +:Date: 1997-08-19 +:Status: incomplete design :Revision: $Id$ :Copyright: See `Copyright and License`_. -:Index terms: pair: pool class mechanism; design +:Index terms: pair: pool classes; design -Definitions ------------ +Introduction +------------- -_`.def.outer-structure`: The "outer structure" (of a pool) is a C -object of type ``PoolXXXStruct`` or the type ``struct PoolXXXStruct`` -itself. - -_`.def.generic-structure`: The "generic structure" is a C object of -type ``PoolStruct`` (found embedded in the outer-structure) or the -type ``struct PoolStruct`` itself. +_`.intro`: This document describes the interface and protocols between +the MPM and the pool classes. -Defaults --------- +Classes and structures +---------------------- -_`.align`: When initialised, the pool gets the default alignment -(``ARCH_ALIGN``). +_`.class`: Each pool belongs to a *pool class*. -_`.no`: If a pool class doesn't implement a method, and doesn't expect -it to be called, it should use a non-method (``PoolNo*``) which will -cause an assertion failure if they are reached. +_`.class.name`: Each pool class has a short, pithy, cryptic name for +the pool class. It should start with ``"A"`` (for "automatic") if +memory is managed by the garbage collector, and ``"M"`` (for "manual") +if memory is managed by alloc/free. For example, "AMC", "MVFF". -_`.triv`: If a pool class supports a protocol but does not require any -more than a trivial implementation, it should use a trivial method -(``PoolTriv*``) which will do the trivial thing. +_`.class.protocol`: Pool classes use the *protocol* mechanisms (see +design.mps.protocol_) to implement class initialization and +inheritance. -_`.outer-structure.sig`: It is good practice to put the signature for -the outer structure at the end (of the structure). This is because -there's already one at the beginning (in the poolStruct) so putting it +.. _design.mps.protocol: protocol + +_`.class.structure`: Each pool class has an associated *class +structure*, which is a C object of type ``PoolClass``. This is +initialized and accessed via the ``CLASS()`` macro, for example +``CLASS(MRGPool)`` initializes and accesses the class structure for +the MRG pool class. + +_`.struct.outer`: The *outer structure* of a pool belonging to the ABC +pool class is a C object of type ``ABCPoolStruct``, which is a typedef +for ``struct PoolABCStruct``. + +_`.stuct.outer.sig`: It is good practice to put the signature for the +outer structure at the end (of the structure). This is because there's +already one at the beginning (in the generic structure), so putting it at the end gives some extra fencepost checking. - -Requirements ------------- - -.. note:: - - Placeholder: must derive the requirements from the architecture. - -_`.req.fix`: ``PoolFix()`` must be fast. +_`.struct.generic`: The *generic structure* of a pool is a C object of +type ``PoolStruct`` (found embedded in the outer structure), which is +a typedef for ``struct PoolStruct``. -Other ------ +Fields +------ -Interface in mpm.h -Types in mpmst.h -See also design.mps.poolclass +_`.field`: These fields are provided by pool classes as part of the +``PoolClass`` object (see `.class.structure`_). They form part of the +interface which allows the MPM to treat pools in a uniform manner. + +_`.field.name`: The ``name`` field must be the pool class name +(`.class.name`_). + +_`.field.size`: The ``size`` field is the size of the pool instance +structure. For the ``PoolABC`` class this can reasonably be expected +to be ``sizeof(PoolABCStruct)``. + +_`.field.attr`: The ``attr`` field must be a bitset of pool class +attributes. See design.mps.type.attr_. + +.. _design.mps.type.attr: type#attr + +_`.field.alignShift`: The ``alignShift`` field is the ``SizeLog2`` of +the pool's alignment. It is computed and initialised when a pool is +created. Mark-and-sweep pool classes use it to compute the number of +grains in a segment, which is the number of bits need in the segment's +mark and alloc bit tables. + +_`.field.format`: The ``format`` field is used to refer to the object +format. The object format is passed to the pool during pool creation. -Document History +Methods +------- + +_`.method`: These methods are provided by pool classes as part of the +``PoolClass`` object (see `.class.structure`_). 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 (which asserts) 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, which does nothing. + +_`.method.inst`: Pool classes may implement the generic instance +methods (see design.mps.protocol.inst.method_). In particular: + +.. _design.mps.protocol.inst.method: inst#method + +- _`.method.inst.finish`: The ``finish`` method + (design.mps.protocol.inst.method.finish_) must finish the outer + structure and then call its superclass method via the + ``NextMethod()`` macro (thus calling ``PoolAbsFinish()`` which + finishes the generic structure). + + .. _design.mps.protocol.inst.method.finish: inst#method.finish + +- _`.method.inst.describe`: The ``describe`` method + (design.mps.protocol.inst.method.describe_) should print a + description of the pool. Each line should begin with two spaces. + Classes are not required to provide this method. + + .. _design.mps.protocol.inst.method.describe: inst#method.describe + +``typedef void (*PoolVarargsMethod)(ArgStruct args[], va_list varargs)`` + +_`.method.varargs`: The ``varargs`` field decodes the variable +arguments to the deprecated function ``mps_pool_create()`` and +converts them to a list of keyword arguments (see +design.mps.keyword-arguments_). + +.. _design.mps.keyword-arguments: keyword-arguments + +``typedef Res (*PoolInitMethod)(Pool pool, Arena arena, PoolClass klass, ArgList args)`` + +_`.method.init`: The ``init`` method must call its superclass method +via the ``NextMethod()`` macro (thus calling ``PoolAbsInit()`` which +initializes the generic structure), and then initialize the outer +structure. It is called via the generic function ``PoolInit()``. + +``typedef Res (*PoolAllocMethod)(Addr *pReturn, Pool pool, Size size)`` + +_`.method.alloc`: The ``alloc`` method manually allocates a block of +at least ``size`` bytes. It should update ``*pReturn`` with a pointer +to a fresh (that is, not overlapping with any other live object) +object of the required size. Failure to allocate must be indicated by +returning an appropriate error code, and in such a case, ``*pReturn`` +must not be updated. Pool classes are not required to provide this +method. It is called via the generic function ``PoolAlloc()``. + +_`.method.alloc.size.align`: A pool class may allow an unaligned +``size`` (rounding it up to the pool's alignment). + +``typedef void (*PoolFreeMethod)(Pool pool, Addr old, Size size)`` + +_`.method.free`: The ``free`` method manually frees a block. The +parameters are required to correspond to a previous allocation request +(possibly via a buffer, not necessarily via ``PoolAlloc()``). It is an +assertion by the client that the indicated object is no longer +required and the resources associated with it can be recycled. Pool +classes are not required to provide this method. It is called via the +generic function ``PoolFree()``. + +_`.method.free.size.align`: A pool class may allow an unaligned +``size`` (rounding it up to the pool's alignment). + +``typedef BufferClass (*PoolBufferClassMethod)(void)`` + +_`.method.bufferClass`: The ``bufferClass`` method returns the class +of buffers used by the pool. Pool classes are not required to provide +this method. It is called via the generic function +``PoolDefaultBufferClass()``. + +``typedef Res (*PoolBufferFillMethod)(Addr *baseReturn, Addr *limitReturn, Pool pool, Buffer buffer, Size size)`` + +_`.method.bufferFill`: The ``bufferFill`` method should allocate a +region of least ``size`` bytes of memory for attaching to ``buffer``. +The buffer is in the "reset" state (see design.mps.buffer.reset_). If +successful, it must update ``*baseReturn`` and ``*limitReturn`` to the +base and limit of the allocated region and return ``ResOK``. Otherwise +it must leave ``*baseReturn`` and ``*limitReturn`` unchanged and +return a non-OK result code. Pool classes are not required to provide +this method. This method is called by ``BufferFill()``. + +.. _design.mps.buffer.reset: buffer#reset + +``typedef void (*PoolBufferEmptyMethod)(Pool pool, Buffer buffer)`` + +_`.method.bufferEmpty`: The ``bufferEmpty`` method indicates that the +client program has finished with the unused part of the buffer (the +part between init and limit). The buffer is in the "ready" state (see +design.mps.buffer.ready_). This method must be provided if and only if +``bufferFill`` is provided. This method is called by the generic +function ``BufferDetach()``. + +.. _design.mps.buffer.ready: buffer#ready + +``typedef Size (*PoolSizeMethod)(Pool pool)`` + +_`.method.totalSize`: The ``totalSize`` method must return the total +memory allocated from the arena and managed by the pool. This method +is called by the generic function ``PoolTotalSize()``. + +_`.method.freeSize`: The ``freeSize`` method must return the free +memory allocated from the arena and managed by the pool, but not in +use by the client program. This method is called by the generic +function ``PoolFreeSize()``. + + +Document history ---------------- -- 1996-07-31 richard incomplete doc +- 1997-08-19 RB_ Initial draft. David Jones added comments about how + accurate this document is. - 2002-06-07 RB_ Converted from MMInfo database design document. -- 2013-05-23 GDR_ Converted to reStructuredText. +- 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/ @@ -78,8 +224,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2013-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 diff --git a/mps/design/poolamc.txt b/mps/design/poolamc.txt index fca51e38da8..aa75d530ec8 100644 --- a/mps/design/poolamc.txt +++ b/mps/design/poolamc.txt @@ -75,11 +75,11 @@ fragment (BEF), large segment padding (LSP), and non-mobile reclaim (NMR). (Large segment pads were new with job001811_.) _`.pad.reason.bef`: Buffer empty fragment (BEF) pads are made by -``AMCBufferEmpty()`` whenever it detaches a non-empty buffer from an -AMC segment. Buffer detachment is most often caused because the buffer -is too small for the current buffer reserve request (which may be -either a client requested or a forwarding allocation). Detachment may -happen for other reasons, such as trace flip. +``amcSegBufferEmpty()`` whenever it detaches a non-empty buffer from +an AMC segment. Buffer detachment is most often caused because the +buffer is too small for the current buffer reserve request (which may +be either a client requested or a forwarding allocation). Detachment +may happen for other reasons, such as trace flip. _`.pad.reason.lsp`: Large segment padding (LSP) pads are made by ``AMCBufferFill()`` when the requested fill size is "large" (see `The @@ -92,7 +92,7 @@ size, ``AMCBufferFill()`` fills any remainder with an large segment pad. _`.pad.reason.nmr`: Non-mobile reclaim (NMR) pads are made by -``amcReclaimNailed()``, when performing reclaim on a non-mobile (that +``amcSegReclaimNailed()``, when performing reclaim on a non-mobile (that is, either boarded or stuck) segment: The more common NMR scenario is reclaim of a boarded segment after a @@ -117,7 +117,7 @@ not distinguishable because there is no nailboard. On reclaim, all objects except forwarding pointers are preserved; each forwarding object is replaced by an NMR pad. -If ``amcReclaimNailed()`` finds no objects to be preserved then it +If ``amcSegReclaimNailed()`` finds no objects to be preserved then it calls ``SegFree()`` (new with job001809_). @@ -137,7 +137,7 @@ Retained pads could be a problem Retained pads are the NMR pads stuck in "from-space": non-mobile segments that were condemned but have preserved-in-place objects -cannot be freed by ``amcReclaimNailed()``. The space around the +cannot be freed by ``amcSegReclaimNailed()``. The space around the preserved objects is filled with NMR pads. In the worst case, retained pads could waste an enormous amount of @@ -169,10 +169,10 @@ more):: /* large */ } -``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->extendBy`` defaults to 4096 (rounded up to the arena +alignment), and is settable by using ``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: @@ -181,7 +181,7 @@ AMC might treat "Large" segments specially, in two ways: any) is immediately padded with an LSP pad. - _`.large.lsp-no-retain`: Nails to such an LSP pad do not cause - AMCReclaimNailed() to retain the segment. + ``amcSegReclaimNailed()`` to retain the segment. `.large.single-reserve`_ is implemented. See job001811_. @@ -204,21 +204,21 @@ indistinguishable from a client object, so AMC has no direct way to detect, and safely ignore, the final LSP object in the seg. If AMC could *guarantee* that the single buffer reserve (`.large.single-reserve`_) is only used for a single *object*, then -``AMCReclaimNailed()`` could honour a nail at the start of a large seg -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 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 -reclaim time. Therefore AMC must record whether or not the last object -in the seg is a pad, in order to ignore nails to it. This could be -done by adding a flag to ``AMCSegStruct``. (This can be done without -increasing the structure size, by making the ``Bool new`` field -smaller than its current 32 bits.) +``amcSegReclaimNailed()`` could honour a nail at the start of a large +seg 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 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 reclaim time. Therefore AMC must record +whether or not the last object in the seg is a pad, in order to ignore +nails to it. This could be done by adding a flag to ``AMCSegStruct``. +(This can be done without increasing the structure size, by making the +``Bool new`` field smaller than its current 32 bits.) The LSP payoff calculation @@ -266,7 +266,7 @@ poolamc.c calculates metrics; see `Feedback about retained pages`_ below. If this one-size-fits-all approach is not satisfactory, ``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`. +``MPS_KEY_LARGE_SIZE`` keyword argument to ``mps_pool_create_k()``. Retained pages @@ -350,7 +350,10 @@ Segments -------- _`.seg.class`: AMC allocates segments of class ``AMCSegClass``, which -is a subclass of ``GCSegClass``. +is a subclass of ``MutatorSegClass`` (see +design.mps.seg.over.hierarchy.mutatorseg_). + +.. _design.mps.seg.over.hierarchy.mutatorseg: seg#over-hierarchy-mutatorseg _`.seg.gen`: AMC organizes the segments it manages into generations. @@ -435,16 +438,12 @@ _`.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 -single padding object, speeding up further scans. - Emergency tracing ----------------- -_`.emergency.fix`: ``AMCFixEmergency()`` is at the core of AMC's -emergency tracing policy (unsurprisingly). ``AMCFixEmergency()`` +_`.emergency.fix`: ``amcSegFixEmergency()`` is at the core of AMC's +emergency tracing policy (unsurprisingly). ``amcSegFixEmergency()`` chooses exactly one of three options: #. use the existing nailboard structure to record the fix; @@ -460,8 +459,8 @@ is used to snapout the pointer. Otherwise it is as for an _`.emergency.scan`: This is basically as before, the only complication is that when scanning a nailed segment we may need to do multiple -passes, as ``FixEmergency()`` may introduce new marks into the nail -board. +passes, as ``amcSegFixEmergency()`` may introduce new marks into the +nail board. Buffers @@ -657,8 +656,9 @@ for small enough ramps. _`.ramp.begin.leave.ramping`: We enter the RAMPING state if a collection starts that condemns the ramp generation (pedantically when a new GC begins, and a segment in the ramp generation is condemned, we -leave the BEGIN state, see AMCWhiten). At this point we switch the -ramp generation to forward to itself (`.gen.ramp.ramping`_). +leave the BEGIN state, see ``amcSegWhiten()``). At this point we +switch the ramp generation to forward to itself +(`.gen.ramp.ramping`_). _`.ramp.ramping.leave`: We leave the RAMPING state and go to the FINISH state when the ramp count goes back to zero. Thus, the FINISH @@ -711,25 +711,23 @@ _`.finish.forward`: If the pool is being destroyed it is OK to destroy the forwarding buffers, as the condemned set is about to disappear. -``void AMCBufferEmpty(Pool pool, Buffer buffer, Addr init, Addr limit)`` +``void amcSegBufferEmpty(Seg seg, Buffer buffer)`` -_`.flush`: Removes the connexion between a buffer and a group, so that -the group is no longer buffered, and the buffer is reset and will -cause a refill when next used. +_`.flush`: Free the unused part of the buffer to the segment. -_`.flush.pad`: The group is padded out with a dummy object so that it -appears full. +_`.flush.pad`: The segment is padded out with a dummy object so that +it appears full. -_`.flush.expose`: The buffer needs exposing before writing the padding -object onto it. If the buffer is being used for forwarding it might -already be exposed, in this case the segment attached to it must be -covered when it leaves the buffer. See `.fill.expose`_. +_`.flush.expose`: The segment needs exposing before writing the +padding object onto it. If the segment is being used for forwarding it +might already be exposed, in this case the segment attached to it must +be covered when it leaves the buffer. See `.fill.expose`_. -_`.flush.cover`: The buffer needs covering whether it was being used +_`.flush.cover`: The segment needs covering whether it was being used for forwarding or not. See `.flush.expose`_. -``Res AMCBufferFill(Addr *baseReturn, Addr *limitReturn, Pool pool, Buffer buffer, Size size, Bool withReservoirPermit)`` +``Res AMCBufferFill(Addr *baseReturn, Addr *limitReturn, Pool pool, Buffer buffer, Size size)`` _`.fill`: Reserve was called on an allocation buffer which was reset, or there wasn't enough room left in the buffer. Allocate a group for @@ -740,9 +738,9 @@ exposed, in which case the group attached to it should be exposed. See `.flush.cover`_. -``Res AMCFix(Pool pool, ScanState ss, Seg seg, Ref *refIO)`` +``Res amcSegFix(Seg seg, ScanState ss, Ref *refIO)`` -_`.fix`: Fix a reference to the pool. +_`.fix`: Fix a reference to an AMC segment. Ambiguous references lock down an entire segment by removing it from old-space and also marking it grey for future scanning. @@ -766,13 +764,13 @@ _`.fix.exact.grey`: The new copy must be at least as grey as the old as it may have been grey for some other collection. -``Res AMCScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg)`` +``Res amcSegScan(Bool *totalReturn, Seg seg, ScanState ss1)`` _`.scan`: Searches for a group which is grey for the trace and scans it. If there aren't any, it sets the finished flag to true. -``void AMCReclaim(Pool pool, Trace trace, Seg seg)`` +``void amcSegReclaim(Seg seg, Trace trace)`` _`.reclaim`: After a trace, destroy any groups which are still condemned for the trace, because they must be dead. @@ -813,8 +811,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2013-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 diff --git a/mps/design/poolams.txt b/mps/design/poolams.txt index ef59e46724a..2f2ec0c1ebd 100644 --- a/mps/design/poolams.txt +++ b/mps/design/poolams.txt @@ -22,12 +22,12 @@ _`.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 +design.mps.action and design.mps.pool_ [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.pool: pool .. _design.mps.scan: scan .. _design.mps.trace: trace .. _design.mps.buffer: buffer @@ -143,7 +143,7 @@ outlawed. _`.colour.alloc`: Objects are allocated black. This is the most efficient alternative for traces in the black mutator phase, and -.not-req.grey means that's sufficient. +`.not-req.grey`_ means that's sufficient. .. note:: @@ -255,9 +255,9 @@ there are grey objects in the segment, because the grey objects might have been subsequently scanned and blackened. _`.marked.fix`: The ``marksChanged`` flag is set ``TRUE`` by -``AMSFix()`` when an object is made grey. +``amsSegFix()`` when an object is made grey. -_`.marked.scan`: ``AMSScan()`` must blacken all grey objects on the +_`.marked.scan`: ``amsSegScan()`` must blacken all grey objects on the segment, so it must iterate over the segment until all grey objects have been seen. Scanning an object in the segment might grey another one (`.marked.fix`_), so the scanner iterates until this flag is @@ -285,9 +285,9 @@ nothing marked as grey, so the ``marksChanged`` flag must already be ``FALSE``. _`.marked.blacken`: When the tracer decides not to scan, but to call -``PoolBlacken()``, we know that any greyness can be removed. -``AMSBlacken()`` does this and resets the ``marksChanged`` flag, if it -finds that the segment has been condemned. +``SegBlacken()``, we know that any greyness can be removed. +``amsSegBlacken()`` does this and resets the ``marksChanged`` flag, if +it finds that the segment has been condemned. _`.marked.clever`: AMS could be clever about not setting the ``marksChanged`` flag, if the fixed object is ahead of the current @@ -325,7 +325,7 @@ current (2002-01) implementation of buffers assumes buffers are black, so they'd better. _`.fill.colour.reclaim`: In fact, putting a buffer on a condemned -segment will screw up the accounting in ``AMCReclaim()``, so it's +segment will screw up the accounting in ``amsSegReclaim()``, so it's disallowed. _`.fill.slow`: ``AMSBufferFill()`` gets progressively slower as more @@ -343,7 +343,7 @@ _`.fill.extend`: If there's no space in any existing segment, the allocate. If that fails, the code tries to allocate a segment that's just large enough to satisfy the request. -_`.empty`: ``AMSBufferEmpty()`` makes the unused space free, since +_`.empty`: ``amsSegBufferEmpty()`` makes the unused space free, since there's no reason not to. We have to adjust the colour tables as well, since these grains were black and now they need to be white (or at least encoded -G and W). @@ -386,11 +386,11 @@ ignored like free space, so need the same encoding). Reclaim ....... -_`.reclaim`: Reclaim uses either of analysis.non-moving-colour -.constraint.reclaim.white-free-bit (just reuse the non-white table as -the alloc table) or +_`.reclaim`: Reclaim uses either of +analysis.non-moving-colour.constraint.reclaim.white-free-bit (just +reuse the non-white table as the alloc table) or analysis.non-moving-colour.constraint.reclaim.free-bit (copy it), -depending on the shareAllocTable flag (as set by `.init.share`_). +depending on the ``shareAllocTable`` flag (as set by `.init.share`_). However, bit table still has to be iterated over to count the free grains. Also, in a debug pool, each white block has to be splatted. @@ -436,7 +436,7 @@ 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 +.. _design.mps.seg.split-merge.fail.anti.no: seg#split-merge-fail-anti-no Testing @@ -462,10 +462,10 @@ index in a segment uses macros such as ``AMS_INDEX`` and every translation -- we could cache that. _`.grey-mutator`: To enforce the restriction set in `.not-req.grey`_ -we check that all the traces are flipped in ``AMSScan()``. It would be -good to check in ``AMSFix()`` as well, but we can't do that, because -it's called during the flip, and we can't tell the difference between -the flip and the grey mutator phases with the current tracer +we check that all the traces are flipped in ``amsSegScan()``. It would +be good to check in ``amsSegFix()`` as well, but we can't do that, +because it's called during the flip, and we can't tell the difference +between the flip and the grey mutator phases with the current tracer interface. @@ -497,8 +497,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2013-2018 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 diff --git a/mps/design/poolawl.txt b/mps/design/poolawl.txt index b8b74820645..b66b8bd1d10 100644 --- a/mps/design/poolawl.txt +++ b/mps/design/poolawl.txt @@ -103,104 +103,62 @@ _`.poolstruct`: The class specific pool structure is:: struct AWLStruct { PoolStruct poolStruct; - Format format; - Shift alignShift; - ActionStruct actionStruct; - double lastCollected; - Serial gen; - Sig sig; + PoolGenStruct pgenStruct; /* pool generation */ + PoolGen pgen; /* NULL or pointer to pgenStruct */ + Count succAccesses; /* number of successive single accesses */ + FindDependentFunction findDependent; /* to find a dependent object */ + awlStatTotalStruct stats; + Sig sig; /* */ } -_`.poolstruct.format`: The format field is used to refer to the object -format. The object format is passed to the pool during pool creation. - -_`.poolstruct.alignshift`: The ``alignShift`` field is the -``SizeLog2`` of the pool's alignment. It is computed and initialised -when a pool is created. It is used to compute the number of alignment -grains in a segment which is the number of bits need in the segment's -mark and alloc bit table (see `.awlseg.bt`_, `.awlseg.mark`_, and -`.awlseg.alloc`_ below). - -.. note:: - - Clarify this. - -_`.poolstruct.actionStruct`: Contains an Action which is used to -participate in the collection benefit protocol. See ``AWLBenefit()`` -below for a description of the algorithm used for determining when to -collect. - -_`.poolstruct.lastCollected`: Records the time (using the mutator -total allocation clock, ie that returned by -``ArenaMutatorAllocSize()``) of the most recent call to either -``AWLInit()`` or ``AWLTraceBegin()`` for this pool. So this is the -time of the beginning of the last collection of this pool. Actually -this isn't true because the pool can be collected without -``AWLTraceBegin()`` being called (I think) as it will get collected by -being in the same zone as another pool/generation that is being -collected (which it does arrange to be, see the use of the gen field -in `.poolstruct.gen`_ below and `.fun.awlsegcreate.where`_ below). - -_`.poolstruct.gen`: This part of the mechanism by which the pool -arranges to be in a particular zone and arranges to be collected -simultaneously with other cohorts in the system. ``gen`` is the -generation that is used in expressing a generation preference when -allocating a segment. The intention is that this pool will get -collected simultaneously with any other segments that are also -allocated using this generation preference (when using the VM arena, -generation preferences get mapped more or less to zones, each -generation to a unique set of zones in the ideal case). Whilst AWL is -not generational it is expected that this mechanism will arrange for -it to be collected simultaneously with some particular generation of -AMC. - -_`.poolstruct.gen.1`: At the moment the ``gen`` field is set for all -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 -pool are instances of this class, and are of type ``AWLSeg``, for +a subclass of ``MutatorSegClass`` (see +design.mps.seg.over.hierarchy.mutatorseg_). All segments allocated by +the pool are instances of this class, and are of type ``AWLSeg``, for which the structure is:: struct AWLSegStruct { - GCSegStruct gcSegStruct; + GCSegStruct gcSegStruct; /* superclass fields must come first */ BT mark; BT scanned; BT alloc; Count grains; - Count free; - Count singleAccesses; - AWLStatSegStruct stats; - Sig sig; + Count freeGrains; /* free grains */ + Count bufferedGrains; /* grains in buffers */ + Count newGrains; /* grains allocated since last collection */ + Count oldGrains; /* grains allocated prior to last collection */ + Count singleAccesses; /* number of accesses processed singly */ + awlStatSegStruct stats; + Sig sig; /* */ } -.. _design.mps.seg.over.hierarchy.gcseg: seg#over.hierarchy.gcseg +.. _design.mps.seg.over.hierarchy.mutatorseg: seg#over-hierarchy-mutatorseg -_`.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 -alignment grain in the pool. +_`.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 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. -Currently there is only one mark bit table. This means that the pool -can only be condemned for one trace. +_`.awlseg.mark`: The ``mark`` bit table is used to record mark bits +during a trace. ``awlSegWhiten()`` (see `.fun.whiten`_ below) sets all +the bits of this table to zero. Fix will read and set bits in this +table. Currently there is only one mark bit table. This means that the +pool can only be condemned for one trace. _`.awlseg.mark.justify`: This is simple, and can be improved later when we want to run more than one trace. -_`.awlseg.scanned`: The scanned bit-table is used to note which +_`.awlseg.scanned`: The ``scanned`` bit-table is used to note which objects have been scanned. Scanning (see `.fun.scan`_ below) a segment will find objects that are marked but not scanned, scan each object found and set the corresponding bits in the scanned table. -_`.awlseg.alloc`: The alloc bit table is used to record which portions -of a segment have been allocated. Ranges of bits in this table are set -when a buffer is attached to the segment. When a buffer is flushed (ie -``AWLBufferEmpty()`` is called) from the segment, the bits +_`.awlseg.alloc`: The ``alloc`` bit table is used to record which +portions of a segment have been allocated. Ranges of bits in this +table are set in ``awlSegBufferFill()`` when a buffer is attached to +the segment. When a buffer is flushed (that is, +``awlSegBufferEmpty()`` is called) from the segment, the bits corresponding to the unused portion at the end of the buffer are reset. @@ -208,16 +166,16 @@ _`.awlseg.alloc.invariant`: A bit is set in the alloc table if and only if the corresponding address is currently being buffered, or the corresponding address lies within the range of an allocated object. -_`.awlseg.grains`: The grains field is the number of grains that fit -in the segment. Strictly speaking this is not necessary as it can be -computed from ``SegSize`` and AWL's alignment, however, precalculating -it and storing it in the segment makes the code simpler by avoiding -lots of repeated calculations. +_`.awlseg.grains`: The ``grains`` field is the number of grains that +fit in the segment. Strictly speaking this is not necessary as it can +be computed from ``SegSize`` and AWL's alignment, however, +precalculating it and storing it in the segment makes the code simpler +by avoiding lots of repeated calculations. -_`.awlseg.free`: A conservative estimate of the number of free grains -in the segment. It is always guaranteed to be greater than or equal to -the number of free grains in the segment, hence can be used during -allocation to quickly pass over a segment. +_`.awlseg.freeGrains`: A conservative estimate of the number of free +grains in the segment. It is always guaranteed to be greater than or +equal to the number of free grains in the segment, hence can be used +during allocation to quickly pass over a segment. .. note:: @@ -241,12 +199,6 @@ _`.fun.init`: ``AWLStruct`` has four fields, each one needs initializing. _`.fun.init.poolstruct`: The ``poolStruct`` field has already been initialized by generic code (impl.c.pool). -_`.fun.init.format`: The format will be copied from the argument list, -checked, and written into this field. - -_`.fun.init.alignshift`: The ``alignShift`` will be computed from the -pool alignment and written into this field. - _`.fun.init.sig`: The ``sig`` field will be initialized with the signature for this pool. @@ -266,48 +218,86 @@ implement free. ``Res AWLBufferFill(Seg *segReturn, Addr *baseReturn, Pool pool, Buffer buffer, Size size)`` _`.fun.fill`: This zips round all the the segments applying -``AWLSegAlloc()`` to each segment that has the same rank as the -buffer. ``AWLSegAlloc()`` attempts to find a free range, if it finds a -range then it may be bigger than the actual request, in which case the -remainder can be used to "fill" the rest of the buffer. If no free -range can be found in an existing segment then a new segment will be -created (which is at least large enough). The range of buffered -addresses is marked as allocated in the segment's alloc table. +``SegBufferFill()`` to each segment. ``awlSegBufferFill()`` attempts +to find a large-enough free range; if it finds one then it may be +bigger than the actual request, in which case the remainder can be +used to "fill" the rest of the buffer. If no free range can be found +in an existing segment then a new segment will be created (which is at +least large enough). The range of buffered addresses is marked as +allocated in the segment's alloc table. -``void AWLBufferEmpty(Pool pool, Buffer buffer)`` +``Res AWLDescribe(Pool pool, mps_lib_FILE *stream, Count depth)`` -_`.fun.empty`: Locates the free portion of the buffer, that is the -memory between the init and the limit of the buffer and records these -locations as being free in the relevant alloc table. The segment that -the buffer is pointing at (which contains the alloc table that needs -to be dinked with) is available via ``BufferSeg()``. +_`.fun.describe`: -_`.fun.benefit`: The benefit returned is the total amount of mutator -allocation minus the ``lastRembemberedSize`` minus 10 MiB, so the pool -becomes an increasingly good candidate for collection at a constant -(mutator allocation) rate, crossing the 0 line when there has been -10 MiB of allocation since the (beginning of the) last collection. So -it gets collected approximately every 10 MiB of allocation. Note that -it will also get collected by virtue of being in the same zone as some -AMC generation (assuming there are instantiated AMC pools), see -`.poolstruct.gen`_ above. -``Res AWLCondemn(Pool pool, Trace trace, Seg seg)`` +Internal +........ -_`.fun.condemn`: The current design only permits each segment to be +``Res AWLSegCreate(AWLSeg *awlsegReturn, Size size)`` + +_`.fun.awlsegcreate`: Creates a segment of class ``AWLSegClass`` of size at least ``size``. + +_`.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 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 +``AWLStruct`` (the ``gen`` field), see `.poolstruct.gen`_ above. + +``Res awlSegInit(Seg seg, Pool pool, Addr base, Size size, ArgList args)`` + +_`.fun.awlseginit`: Init method for ``AWLSegClass``, called for +``SegAlloc()`` whenever an ``AWLSeg`` is created (see +`.fun.awlsegcreate`_ above). + +_`.fun.awlseginit.tables`: The segment's mark scanned and alloc tables +(see `.awlseg.bt`_ above) are allocated and initialised. The segment's +grains field is computed and stored. + +``void awlSegFinish(Seg seg)`` + +_`.fun.awlsegfinish`: Finish method for ``AWLSegClass``, called from +``SegFree()``. Will free the segment's tables (see `.awlseg.bt`_). + +``Bool awlSegBufferFill(Addr *baseReturn, Addr *limitReturn, Seg seg, Size size, RankSet rankSet)`` + +_`.fun.seg.buffer-fill`: Searches for a free block in the segment that +is at least ``size`` bytes long. The base address of the block is +returned in ``*baseReturn``, the limit of the entire free block (which +must be at least as large as ``size`` and may be bigger) is returned +in ``*limitReturn``. The requested size is converted to a number of +grains, ``BTFindResRange()`` is called to find a run of this length in +the alloc bit-table (`.awlseg.alloc`_). The results (if it is +successful) from ``BTFindResRange()`` are in terms of grains, they are +converted back to addresses before returning the relevant values from +this function. + +``void awlSegBufferEmpty(Seg seg, Buffer buffer)`` + +_`.fun.seg.buffer-empty`: Locates the free portion of the buffer, that +is the memory between the init and the limit of the buffer and records +these locations as being free in the alloc table. + +``Res awlSegWhiten(Seg seg, Trace trace)`` + +_`.fun.whiten`: The current design only permits each segment to be condemned for one trace (see `.awlseg.mark`_). This function checks -that the segment is not condemned for any trace (``seg->white == +that the segment is not white for any trace (``seg->white == TraceSetEMPTY``). The segment's mark bit-table is reset, and the whiteness of the seg (``seg->white``) has the current trace added to it. -``void AWLGrey(Pool pool, Trace trace, Seg seg)`` +``void awlSegGreyen(Seg seg, Trace trace)`` -_`.fun.grey`: If the segment is not condemned for this trace the +_`.fun.grey`: If the segment is not white for this trace, the segment's mark table is set to all 1s and the segment is recorded as being grey. -``Res AWLScan(ScanState ss, Pool pool, Seg seg)`` +``Res awlSegScan(Bool *totalReturn, Seg seg, ScanState ss)`` _`.fun.scan`: @@ -406,38 +396,31 @@ _`.fun.scan.pass.more`: At the end of a pass the finished flag is examined. _`.fun.scan.pass.more.not`: If the finished flag is set then we are -done (see `.fun.scan.overview.finished-flag`_ above), ``AWLScan()`` +done (see `.fun.scan.overview.finished-flag`_ above), ``awlSegScan()`` returns. _`.fun.scan.pass.more.so`: Otherwise (the finished flag is reset) we perform another pass (see `.fun.scan.pass`_ above). -``Res AWLFix(Pool pool, ScanState ss, Seg seg, Ref *refIO)`` +``Res awlSegFix(Seg seg, ScanState ss, Ref *refIO)`` -_`.fun.fix`: ``ss->wasMarked`` is set to ``TRUE`` (clear compliance -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. - -If the rank (``ss->rank``) is ``RankAMBIG`` then fix returns -immediately unless the referenced grain is allocated. +_`.fun.fix`: If the rank (``ss->rank``) is ``RankAMBIG`` then fix +returns immediately unless the reference is in the segment bounds, +aligned to the pool alignment, and allocated. The bit in the marked table corresponding to the referenced grain will be read. If it is already marked then fix returns. Otherwise (the -grain is unmarked), ``ss->wasMarked`` is set to ``FALSE``, the -remaining actions depend on whether the rank (``ss->rank``) is -``RankWEAK`` or not. If the rank is weak then the reference is -adjusted to 0 (see design.mps.weakness) and fix returns. If the rank -is something else then the mark bit corresponding to the referenced -grain is set, and the segment is greyed using ``TraceSegGreyen()``. +grain is unmarked), ``ss->wasMarked`` is set to ``FALSE`` (see +design.mps.fix.was-marked.not_), the remaining actions depend on +whether the rank (``ss->rank``) is ``RankWEAK`` or not. If the rank is +weak then the reference is adjusted to 0 (see design.mps.weakness) and +fix returns. If the rank is something else then the mark bit +corresponding to the referenced grain is set, and the segment is +greyed using ``SegSetGrey()``. -Fix returns. +.. _design.mps.fix.was-marked.not: fix#was-marked-not - -``void AWLReclaim(Pool pool, Trace trace, Seg seg)`` +``void awlSegReclaim(Seg seg, Trace trace)`` _`.fun.reclaim`: This iterates over all allocated objects in the segment and frees objects that are not marked. When this iteration is @@ -459,56 +442,6 @@ 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, Count depth)`` - -_`.fun.describe`: - - -Internal -........ - -``Res AWLSegCreate(AWLSeg *awlsegReturn, Size size)`` - -_`.fun.awlsegcreate`: Creates a segment of class ``AWLSegClass`` of size at least ``size``. - -_`.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 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 -``AWLStruct`` (the ``gen`` field), see `.poolstruct.gen`_ above. - -``Res awlSegInit(Seg seg, Pool pool, Addr base, Size size, Bool reservoirPermit, va_list args)`` - -_`.fun.awlseginit`: Init method for ``AWLSegClass``, called for -``SegAlloc()`` whenever an ``AWLSeg`` is created (see -`.fun.awlsegcreate`_ above). - -_`.fun.awlseginit.tables`: The segment's mark scanned and alloc tables -(see `.awlseg.bt`_ above) are allocated and initialised. The segment's -grains field is computed and stored. - -``void awlSegFinish(Seg seg)`` - -_`.fun.awlsegfinish`: Finish method for ``AWLSegClass``, called from -``SegFree()``. Will free the segment's tables (see `.awlseg.bt`_). - -``Bool AWLSegAlloc(Addr *baseReturn, Addr *limitReturn, AWLSeg awlseg, AWL awl, Size size)`` - -_`.fun.awlsegalloc`: Will search for a free block in the segment that -is at least size bytes long. The base address of the block is returned -in ``*baseReturn``, the limit of the entire free block (which must be -at least as large size and may be bigger) is returned in -``*limitReturn``. The requested size is converted to a number of -grains, ``BTFindResRange()`` is called to find a run of this length in -the alloc bit-table (`.awlseg.alloc`_). The return results (if it is -successful) from ``BTFindResRange()`` are in terms of grains, they are -converted back to addresses before returning the relevant values from -this function. - ``Bool AWLDependentObject(Addr *objReturn, Addr parent)`` _`.fun.dependent-object`: This function abstracts the association @@ -565,8 +498,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2013-2018 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 diff --git a/mps/design/poollo.txt b/mps/design/poollo.txt index ebb402c316b..05c91ee14fd 100644 --- a/mps/design/poollo.txt +++ b/mps/design/poollo.txt @@ -116,21 +116,17 @@ _`.poolstruct`: The class specific pool structure is:: typedef struct LOStruct { PoolStruct poolStruct; /* generic pool structure */ - Format format; /* format for allocated objects */ - Shift alignShift; - Sig sig; /* impl.h.misc.sig */ + PoolGenStruct pgenStruct; /* pool generation */ + PoolGen pgen; /* NULL or pointer to pgenStruct */ + Sig sig; /* */ } LOStruct; -_`.poolstruct.format`: This is the format of the objects that are -allocated in the pool. +_`.loseg`: Every segment is an instance of segment class +``LOSegClass``, a subclass of ``MutatorSegClass`` (see +design.mps.seg.over.hierarchy.mutatorseg_), and is an object of type +``LOSegStruct``. -_`.poolstruct.alignShift`: This is shift used in alignment -computations. It is ``SizeLog2(pool->alignment).`` It can be used on -the right of a shift operator (``<<`` or ``>>``) to convert between a -number of bytes and a number of grains. - -_`.loseg`: Every segment is an instance of segment class ``LOSegClass``, a -subclass of ``GCSegClass``, and is an object of type ``LOSegStruct``. +.. _design.mps.seg.over.hierarchy.mutatorseg: seg#over-hierarchy-mutatorseg _`.loseg.purpose`: The purpose of the ``LOSeg`` structure is to associate the bit tables used for recording allocation and mark @@ -140,11 +136,13 @@ _`.loseg.decl`: The declaration of the structure is as follows:: typedef struct LOSegStruct { GCSegStruct gcSegStruct; /* superclass fields must come first */ - LO lo; /* owning LO */ BT mark; /* mark bit table */ BT alloc; /* alloc bit table */ - Count free; /* number of free grains */ - Sig sig; /* impl.h.misc.sig */ + Count freeGrains; /* free grains */ + Count bufferedGrains; /* grains in buffers */ + Count newGrains; /* grains allocated since last collection */ + Count oldGrains; /* grains allocated prior to last collection */ + Sig sig; /* */ } LOSegStruct; _`.loseg.sig`: The signature for a loseg is 0x519705E9 (SIGLOSEG). @@ -160,7 +158,7 @@ but might be inefficient in terms of space in some circumstances. _`.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 +this table. When ``loSegFix()`` (see `.fun.fix`_ below) is called the address is converted to a grain within the segment and the corresponding bit in this table is set. @@ -198,7 +196,11 @@ _`.fun.buffer-empty`: _`.fun.condemn`: -``Res LOFix(Pool pool, ScanState ss, Seg seg, Ref *refIO)`` + +Internal +........ + +``Res loSegFix(Seg seg, ScanState ss, Ref *refIO)`` _`.fun.fix`: Fix treats references of most ranks much the same. There is one mark table that records all marks. A reference of rank @@ -213,16 +215,7 @@ been marked otherwise nothing happens. Note that there is no check that the reference refers to a valid object boundary (which wouldn't be a valid check in the case of ambiguous references anyway). -``void LOReclaim(Pool pool, Trace trace, Seg seg)`` - -_`.fun.reclaim`: Derives the loseg from the seg, and calls -``loSegReclaim()`` (see `.fun.segreclaim`_ below). - - -Internal -........ - -``void loSegReclaim(LOSeg loseg, Trace trace)`` +``void loSegReclaim(Seg seg, Trace trace)`` _`.fun.segreclaim`: For all the contiguous allocated regions in the segment it locates the boundaries of all the objects in that region by @@ -231,7 +224,7 @@ of the region (the beginning of the region is guaranteed to coincide with the beginning of an object). For each object it examines the bit in the mark bit table that corresponds to the beginning of the object. If that bit is set then the object has been marked as a result of a -previous call to ``LOFix()``, the object is preserved by doing +previous call to ``loSegFix()``, the object is preserved by doing nothing. If that bit is not set then the object has not been marked and should be reclaimed; the object is reclaimed by resetting the appropriate range of bits in the segment's free bit table. @@ -265,8 +258,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +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 diff --git a/mps/design/poolmfs.txt b/mps/design/poolmfs.txt index 31fe8c24cd9..10076e537de 100644 --- a/mps/design/poolmfs.txt +++ b/mps/design/poolmfs.txt @@ -28,6 +28,31 @@ sizes. The size of object that an instance can manage is declared when the instance is created. +Implementation +-------------- + +_`.impl.extents`: MFS operates in a very simple manner: each extent +allocated from the arena is divided into units. + +_`.impl.free-units`: Free units are kept on a linked list using a +header stored in the unit itself. The linked list is not ordered; +allocation and deallocation simply pop and push from the head of the +list. This is fast, but successive allocations might have poor +locality if previous successive frees did. + +_`.impl.extent-ring`: The list of extents belonging to the pool is +maintained as a ring with a node at the start of each extent. + +_`.impl.extent-ring.justify`: Storing the linked list of free nodes +and the extent ring node in the managed memory is against the general +principle of the MPS design, which keeps its management structures +away from client memory. However, the MFS pool is used during the +bootstrapping process (see design.mps.bootstrap.land.sol.pool_) and so +has no other memory pools available for storage. + +.. _design.mps.bootstrap.land.sol.pool: bootstrap#land-sol-pool + + Document History ---------------- @@ -37,6 +62,9 @@ Document History - 2013-05-23 GDR_ Converted to reStructuredText. +- 2016-03-18 RB_ Moved design text from leader comment of poolmfs.c. + Explained chaining of extents using an embedded ring node. + .. _RB: http://www.ravenbrook.com/consultants/rb/ .. _GDR: http://www.ravenbrook.com/consultants/gdr/ @@ -44,8 +72,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2013-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 diff --git a/mps/design/poolmrg.txt b/mps/design/poolmrg.txt index 148e016fae7..69280a30301 100644 --- a/mps/design/poolmrg.txt +++ b/mps/design/poolmrg.txt @@ -148,7 +148,7 @@ MPS detects that objects are finalizable. _`.over.message`: ``PoolClassMRG`` implements a ``MessageClass`` (see design.mps.message_). All the messages are of one ``MessageType``. This -type is ``MessageTypeFinalization``. Messages are created when objects +type is ``MessageTypeFINALIZATION``. Messages are created when objects are discovered to be finalizable and destroyed when the MPS client has received the message. @@ -183,7 +183,7 @@ 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: finalize#int.finalize +.. _design.mps.finalize.int.finalize: finalize#int-finalize Finalizer execution @@ -204,7 +204,7 @@ _`.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_). -.. _design.mps.finalize.int.arena.struct: 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()``. @@ -336,7 +336,7 @@ the latter will be used to store the link parts of guardians (see one of each class, by the function ``MRGSegPairCreate()``. Each segment contains a link to its pair. -_`.mrgseg.ref`: ``MRGRefSegClass`` is a subclass of ``GCSegClass``. +_`.mrgseg.ref`: ``MRGRefSegClass`` is a subclass of ``MutatorSegClass``. Instances are of type ``MRGRefSeg``, and contain: - _`.mrgseg.ref.mrgring`: a field for the ring of ref part segments in @@ -432,9 +432,9 @@ to grow very quickly. _`.finish`: Iterate over all the segments, returning all the segments to the arena. -``Res MRGScan(Bool *totalReturn, ScanState ss, Pool pool, Seg seg)`` +``Res mrgRefSegScan(Bool *totalReturn, Pool pool, Seg seg, ScanState ss)`` -_`.scan`: ``MRGScan()`` scans a segment. +_`.scan`: ``mrgRefSegScan()`` scans a segment of guardians. _`.scan.trivial`: Scan will do nothing (that is, return immediately) if the tracing rank is anything other than final. @@ -451,10 +451,10 @@ scanning is detrimental, it will only delay finalization. If the rank is higher than final there is nothing to do, the pool only contains final references. -_`.scan.guardians`: ``MRGScan()`` will iterate over all guardians in -the segment. Every guardian's reference will be fixed (_`.scan.free`: -note that guardians that are on the free list have ``NULL`` in their -reference part). +_`.scan.guardians`: ``mrgRefSegScan()`` will iterate over all +guardians in the segment. Every guardian's reference will be fixed +(_`.scan.free`: note that guardians that are on the free list have +``NULL`` in their reference part). _`.scan.wasold`: If the object referred to had not been fixed previously (that is, was unmarked) then the object is not referenced @@ -489,14 +489,6 @@ 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()``, -``TraceBegin()``, ``TraceCondemn()``, ``PoolFix()``, ``PoolReclaim()``, ``TraceEnd()``. - -_`.functions.trivial`: The Grey method of the pool class will be -``PoolTrivGrey()``, this pool has no further bookkeeping to perform -for grey segments. - Transgressions -------------- @@ -512,7 +504,7 @@ arena. A suggested strategy for this is as follows: - Add a free segment ring to the pool. -- In ``MRGRefSegScan()``, if the segment is entirely free, don't scan +- In ``mrgRefSegScan()``, if the segment is entirely free, don't scan it, but instead detach its links from the free ring, and move the segment to the free segment ring. @@ -658,7 +650,7 @@ and haven't been finalized. This will test `.promise.unreachable`_ and Notes ----- -_`.access.inadequate`: ``PoolAccess()`` will scan segments at +_`.access.inadequate`: ``SegAccess()`` will scan segments at `RankEXACT``. Really it should be scanned at whatever the minimum rank of all grey segments is (the trace rank phase), however there is no way to find this out. As a consequence we will sometimes scan pages at @@ -685,8 +677,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2013-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 diff --git a/mps/design/poolmv.txt b/mps/design/poolmv.txt deleted file mode 100644 index 1b25c441a27..00000000000 --- a/mps/design/poolmv.txt +++ /dev/null @@ -1,87 +0,0 @@ -.. mode: -*- rst -*- - -MV pool class -============= - -:Tag: design.mps.poolmv -:Author: Richard Brooksby -:Date: 1995-08-25 -:Status: incomplete design -:Revision: $Id$ -:Copyright: See `Copyright and License`_. -:Index terms: - pair: MV pool class; design - single: pool class; MV design - - -Implementation --------------- - -_`.lost`: It is possible for MV to "lose" memory when freeing an -objects. This happens when an extra block descriptor is needed (ie the -interior of a block is being freed) and the call to allocate the block -fails. - - -References ----------- - -.. [Pugh_1990] William Pugh. 1990. Skip lists: a probabilistic - alternative to balanced trees. Commun. ACM 33, 6 (June 1990), 668-676. - DOI=10.1145/78973.78977 http://doi.acm.org/10.1145/78973.78977 - - -Document History ----------------- - -- 1995-08-25 RB_. Incomplete design. - -- 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-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/mps/design/poolmvff.txt b/mps/design/poolmvff.txt index d29efc6ba89..6eb2004ea01 100644 --- a/mps/design/poolmvff.txt +++ b/mps/design/poolmvff.txt @@ -22,9 +22,10 @@ pool class. This pool implements a first (or last) fit policy for variable-sized manually-managed objects, with control over first/last, segment preference high/low, and slot fit low/high. -The pool was created in a response to a belief that the ScriptWorks -EPDL/EPDR's first fit policy is beneficial for some classes of client -behaviour, but the performance of a linear free list was unacceptable. +_`.background`: The pool was created in a response to a belief that +the ScriptWorks EPDL/EPDR's first fit policy is beneficial for some +classes of client behaviour, but the performance of a linear free list +was unacceptable. Overview @@ -115,8 +116,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +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 diff --git a/mps/design/poolmvt.txt b/mps/design/poolmvt.txt index 59939535604..4df204e46d9 100644 --- a/mps/design/poolmvt.txt +++ b/mps/design/poolmvt.txt @@ -28,12 +28,10 @@ _`.readership`: MM developers _`.source`: req.dylan(6), req.epcore(16), req.product(2) -_`.background`: design.mps.poolmv_, 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`_. -.. _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 @@ -793,8 +791,8 @@ 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.cbs.function.cbs.delete.fail: cbs#function.cbs.delete.fail -.. _design.mps.cbs.function.cbs.insert.fail: cbs#function.cbs.insert.fail +.. _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 @@ -956,12 +954,10 @@ _`.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 MV (design.mps.poolmv_) and +_`.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. diff --git a/mps/design/prmc.txt b/mps/design/prmc.txt index c7aaad52d57..06d91662d7e 100644 --- a/mps/design/prmc.txt +++ b/mps/design/prmc.txt @@ -1,7 +1,7 @@ .. mode: -*- rst -*- -Protection mutator context -========================== +Mutator context +=============== :Tag: design.mps.prmc :Author: Gareth Rees @@ -9,31 +9,30 @@ Protection mutator context :Status: complete design :Revision: $Id$ :Copyright: See `Copyright and License`_. -:Index terms: pair: protection mutator context; design +:Index terms: pair: mutator context; design Introduction ------------ -_`.intro`: This is the design of the protection mutator context -module. +_`.intro`: This is the design of the 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. +_`.overview`: The 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 +_`.status`: The 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 @@ -64,18 +63,30 @@ 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 +_`.req.suspend.scan`: Must capture 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 +.. _design.mps.thread-manager.if.scan: thread-manager#if-scan Interface --------- -``typedef MutatorFaultContextStruct *MutatorFaultContext`` +``typedef unsigned MutatorContextVar`` + +_`.if.var`: The type ``MutatorContextVar`` is the type of the +discriminator for the union within ``MutatorContextStruct``: + +======================== ================================================ +Value Description +======================== ================================================ +``MutatorContextFAULT`` Context of thread stopped by a protection fault. +``MutatorContextTHREAD`` Context of thread stopped by the thread manager. +======================== ================================================ + +``typedef MutatorContextStruct *MutatorContext`` _`.if.context`: A structure representing the context of the mutator at the point when a protection fault occurred, or when it was suspended @@ -83,16 +94,37 @@ 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 +.. _design.mps.thread-manager.if.thread: thread-manager#if-thread -``Bool ProtCanStepInstruction(MutatorFaultContext context)`` +``Bool MutatorContextCheck(MutatorContext context)`` + +_`.if.check`: The check function for mutator contexts. See +design.mps.check_. + +.. _design.mps.check: check + +``Res MutatorContextInitFault(MutatorContext context, ...)`` + +_`.if.init.thread`: Initialize with the context of the mutator at the +point where it was stopped by a protection fault. The arguments are +platform-specific and the return may be ``void`` instead of ``Res`` if +this always succeeds. + +``Res MutatorContextInitThread(MutatorContext context, ...)`` + +_`.if.init.thread`: Initialize with the context of the mutator at the +point where it was suspended by the thread manager. The arguments are +platform-specific and the return may be ``void`` instead of ``Res`` if +this always succeeds. + +``Bool MutatorContextCanStepInstruction(MutatorContext 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. +fault. Return ``TRUE`` if ``MutatorContextStepInstruction()`` is +capable of single-stepping the instruction, or ``FALSE`` if not. -``Bool Res ProtStepInstruction(MutatorFaultContext context)`` +``Bool Res MutatorContextStepInstruction(MutatorContext context)`` _`.if.step`: Single-step the instruction which is causing the fault. Update the mutator context according to the emulation or execution of @@ -101,16 +133,16 @@ 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``. +This function is only called if +``MutatorContextCanStepInstruction(context)`` returned ``TRUE``. -``Res MutatorFaultContextScan(ScanState ss, MutatorFaultContext context, mps_area_scan_t scan, void *closure)`` +``Res MutatorContextScan(ScanState ss, MutatorContext context, mps_area_scan_t scan, void *closure)`` _`.if.context.scan`: Scan all roots found in ``context`` using the given scan state by calling ``scan``, and return the result code from the scanner. -``Addr MutatorFaultContextSP(MutatorFaultContext context)`` +``Addr MutatorContextSP(MutatorContext 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 @@ -123,31 +155,31 @@ Implementations Generic implementation ...................... -_`.impl.an`: In ``prmcan.c``. +_`.impl.an`: In ``prmcan.c`` and ``prmcanan.c``. _`.impl.an.context`: There is no definition of -``MutatorFaultContextStruct`` and so the mutator context cannot be -decoded. +``MutatorContextStruct`` 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 +.. _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 +.. _design.mps.thread-manager.impl.an: thread-manager#impl-an -Unix implementation -................... +Posix 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`: In ``prmcix.c`` and ``protsgix.c``, with +processor-specific parts in ``prmci3.c`` and ``prmci6.c``, and other +platform-specific parts in ``prmcfri3.c``, ``prmclii3.c``, +``prmcfri6.c``, and ``prmclii6.c``. _`.impl.ix.context`: The context consists of the |siginfo_t|_ and |ucontext_t|_ structures. POSIX specifies some of the fields in @@ -159,6 +191,13 @@ This is decoded on a platform-by-platform basis. .. |ucontext_t| replace:: ``ucontext_t`` .. _ucontext_t: http://pubs.opengroup.org/onlinepubs/9699919799/functions/sigaction.html +_`.impl.ix.fault.signal`: POSIX specifies that "Invalid permissions +for mapped object" (a protection fault) causes a ``SEGV`` signal. + +_`.impl.ix.fault.code`: POSIX specifies that "Invalid permissions for +mapped object" (a protection fault) causes ``siginfo_t.si_code`` to be +set to ``SEGV_ACCERR``. + _`.impl.ix.fault.addr`: POSIX specifies that ``siginfo_t.si_addr`` is the address that the faulting instruction was attempting to access. @@ -185,8 +224,9 @@ _`.impl.ix.context.sp`: The stack pointer is obtained from Windows implementation ...................... -_`.impl.w3`: In ``proti3.c``, ``proti6.c``, ``prmci3w3.c``, and -``prmci6w3.c``. +_`.impl.w3`: In ``prmcw3.c``, with processor-specific parts in +``prmci3.c``, ``prmci6.c``, and other platform-specific parts in +``prmcw3i3.c`` and ``prmcw3i6.c``. _`.impl.w3.context`: The context of a thread that hit a protection fault is given by the |EXCEPTION_POINTERS|_ structure passed to a @@ -223,12 +263,12 @@ _`.impl.w3.context.sp`: The stack pointer is obtained from ``CONTEXT.Esp`` (on IA-32) or ``CONTEXT.Rsp`` (on x86-64). -OS X implementation -................... +macOS 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`: In ``prmcix.c`` and ``prmcxc.c``, with processor-specific +parts in ``prmci3.c`` and ``prmci6.c``, and other platform-specific +parts in ``prmcxci3.c`` and ``prmcxci6.c``. _`.impl.xc.context`: The context consists of the ``__Request__mach_exception_raise_state_identity_t`` and @@ -271,8 +311,8 @@ Document History Copyright and License --------------------- -Copyright © 2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2014-2018 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 diff --git a/mps/design/prot.txt b/mps/design/prot.txt index fb06b78b328..1a7cc26a091 100644 --- a/mps/design/prot.txt +++ b/mps/design/prot.txt @@ -55,19 +55,19 @@ 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.sync`: If memory protection is not available, the only way to +meet `.req.consistent`_ is to ensure that no protection is required, +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 +.. _design.mps.prmc.req.fault.addr: prmc#req-fault-addr +.. _design.mps.prmc.req.fault.access: prmc#req-fault-access Interface @@ -131,17 +131,15 @@ 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_). -.. _design.mps.prmc.req.fault.step: prmc#req.fault.step +.. _design.mps.prmc.req.fault.step: prmc#req-fault-step -_`.impl.ix`: POSIX implementation. +_`.impl.ix`: POSIX implementation. See design.mps.protix_. -_`.impl.li`: Linux implementation. See design.mps.protli_. - -.. _design.mps.protli: protli +.. _design.mps.protix: protix _`.impl.w3`: Windows implementation. -_`.impl.xc`: OS X implementation. +_`.impl.xc`: macOS implementation. Document History @@ -153,8 +151,8 @@ 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. +- 2014-10-23 GDR_ Move mutator context interface to design.mps.prmc_. + Bring design up to date. .. _design.mps.prmc: prmc @@ -165,8 +163,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2013-2018 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 diff --git a/mps/design/protli.txt b/mps/design/protix.txt similarity index 62% rename from mps/design/protli.txt rename to mps/design/protix.txt index 8d4a2f7b395..9fc8a41b784 100644 --- a/mps/design/protli.txt +++ b/mps/design/protix.txt @@ -1,17 +1,17 @@ .. mode: -*- rst -*- -Linux implementation of protection module +POSIX implementation of protection module ========================================= -:Tag: design.mps.protli +:Tag: design.mps.protix :Author: Tony Mann :Date: 2000-02-03 :Status: incomplete document :Revision: $Id$ :Copyright: See `Copyright and License`_. :Index terms: - pair: Linux; protection interface design - pair: Linux protection interface; design + pair: POSIX; protection interface design + pair: POSIX protection interface; design Introduction @@ -19,9 +19,9 @@ Introduction _`.readership`: Any MPS developer -_`.intro`: This is the design of the Linux implementation of the -protection module. It makes use of various services provided by Linux. -It is intended to work with LinuxThreads. +_`.intro`: This is the design of the POSIX implementation of the +protection module. It makes use of various services provided by POSIX. +It is intended to work with POSIX Threads. Requirements @@ -36,12 +36,13 @@ interface defined in design.mps.prot.if_. Data structures --------------- -_`.data.signext`: This is static. Because that is the only -communications channel available to signal handlers. - -.. note:: - - Write a little more here. +_`.data.signext`: If the SIGSEGV signal is not handled by any MPS +arena, ``sigHandle()`` needs to forward the signal to the next signal +handler in the chain (the signal handler that was installed when the +``ProtSetup()`` was called), by temporarily reinstalling the old +signal handler and calling ``kill()``. The only way to pass the next +signal handler to the current signal handler is via a global variable, +in this case the variable ``sigNext``. Functions @@ -53,19 +54,6 @@ is the function ``sigHandle()``). The previous handler is recorded (in the variable ``sigNext``, see `.data.signext`_) so that it can be reached from ``sigHandle()`` if it fails to handle the fault. -_`.fun.setup.problem`: The problem with this approach is that we can't -honour the wishes of the ``sigvec(2)`` entry for the previous handler -(in terms of masks in particular). - -_`.improve.sigvec`: What if when we want to pass on the signal instead -of calling the handler we call ``sigvec()`` with the old entry and use -``kill()`` to send the signal to ourselves and then restore our -handler using ``sigvec()`` again? - -.. note:: - - Need more detail and analysis here. - _`.fun.set`: ``ProtSet()`` uses ``mprotect()`` to adjust the protection for pages. @@ -79,7 +67,7 @@ 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 +.. _design.mps.prot.if.set: prot#if-set _`.fun.set.assume.mprotect`: We assume that the call to ``mprotect()`` always succeeds. @@ -97,7 +85,7 @@ Threads ------- _`.threads`: The design must operate in a multi-threaded environment -(with LinuxThreads) and cooperate with the Linux support for locks +(with POSIX Threads) and cooperate with the POSIX support for locks (see design.mps.lock_) and the thread suspension mechanism (see design.mps.pthreadext_ ). @@ -110,18 +98,17 @@ as required by the design (see 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 +.. _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 -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. +_`.threads.async`: POSIX imposes some restrictions on signal handler +functions (see 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 +.. _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 @@ -136,55 +123,34 @@ it's OK to call arbitrary POSIX functions inside the handler. _`.threads.async.other`: If the signal handler is invoked for some other reason (that is, one we are not prepared to handle) then there -is less we can say about what might have caused the SEGV. In general -it is not safe to call arbitrary POSIX functions inside the handler in -this case. +is less we can say about what might have caused the SIGSEGV. In +general it is not safe to call arbitrary POSIX functions inside the +handler in this case. _`.threads.async.choice`: The signal handler calls ``ArenaAccess()`` to determine whether the segmentation fault was the result of an MPS -access. ArenaAccess will claim various MPS locks (that is, the arena -ring lock and some arena locks). The code calls no other POSIX +access. ``ArenaAccess()`` will claim various MPS locks (that is, the +arena ring lock and some arena locks). The code calls no other POSIX functions in the case where the segmentation fault is not an MPS access. The locks are implemented as mutexes and are claimed by calling ``pthread_mutex_lock()``, which is not defined to be async-signal safe. -_`.threads.async.choice.ok`: However, despite the fact that PThreads -documentation doesn't define the behaviour of ``pthread_mutex_lock()`` -in these circumstances, we expect the LinuxThreads implementation will -be well-behaved unless the segmentation fault occurs while while in -the process of locking or unlocking one of the MPS locks (see -`.threads.async.linux-mutex`_). But we can assume that a segmentation -fault will not happen then (because we use the locks correctly, and -generally must assume that they work). Hence we conclude that it is OK -to call ``ArenaAccess()`` directly from the signal handler. - -_`.threads.async.linux-mutex`: A study of the LinuxThreads source code -reveals that mutex lock and unlock functions are implemented as a -spinlock (using a locked compare-and-exchange instruction) with a -backup suspension mechanism using ``sigsuspend()``. On locking, the -spinlock code performs a loop which examines the state of the lock, -and then atomically tests that the state is unchanged while attempting -to modify it. This part of the code is reentrant (and hence -async-signal safe). Eventually, when locking, the spinlock code may -need to block, in which case it calls ``sigsuspend()``, waiting for -the manager thread to unblock it. The unlocking code is similar, -except that this code may need to release another thread, in which -case it calls ``kill()``. The functions ``sigsuspend()`` and -``kill()`` are both defined to be async-signal safe by POSIX. In -summary, the mutex locking functions use primitives which are entirely -async-signal safe. They perform side-effects which modify the fields -of the lock structure only. This code may be safely invoked inside a -signal handler unless the interrupted function is in the process of -manipulating the fields of that lock structure. +_`.threads.async.choice.ok`: However, despite the fact that POSIX +Threads documentation doesn't define the behaviour of +``pthread_mutex_lock()`` in these circumstances, we expect the POSIX +Threads implementation will be well-behaved unless the segmentation +fault occurs while while in the process of locking or unlocking one of +the MPS locks. But we can assume that a segmentation fault will not +happen then (because we use the locks correctly, and generally must +assume that they work). Hence we conclude that it is OK to call +``ArenaAccess()`` directly from the signal handler. _`.threads.async.improve`: In future it would be preferable to not -have to assume reentrant mutex locking and unlocking functions. By -making the assumption we also assume that the implementation of -mutexes in LinuxThreads will not be completely re-designed in future -(which is not wise for the long term). An alternative approach would -be necessary anyway when supporting another platform which doesn't -offer reentrant locks (if such a platform does exist). +have to assume reentrant mutex locking and unlocking functions. An +alternative approach would be necessary anyway when supporting another +platform which doesn't offer reentrant locks (if such a platform does +exist). _`.threads.async.improve.how`: We could avoid the assumption if we had a means of testing whether an address lies within an arena chunk @@ -198,7 +164,7 @@ datastructure. _`.threads.sig-stack`: We do not handle signals on a separate signal stack. Separate signal stacks apparently don't work properly with -Pthreads. +POSIX Threads. Document History @@ -210,6 +176,8 @@ Document History - 2013-05-23 GDR_ Converted to reStructuredText. +- 2016-10-13 GDR_ Generalise to POSIX, not just Linux. + .. _RB: http://www.ravenbrook.com/consultants/rb/ .. _GDR: http://www.ravenbrook.com/consultants/gdr/ @@ -217,8 +185,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2013-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 diff --git a/mps/design/protocol.txt b/mps/design/protocol.txt index bddebf3f2a3..98a1f4ecd7a 100644 --- a/mps/design/protocol.txt +++ b/mps/design/protocol.txt @@ -8,18 +8,16 @@ Protocol inheritance :Date: 1998-10-12 :Status: incomplete design :Revision: $Id$ -:Copyright: See `Copyright and License`_. +:Copyright: See `C. Copyright and License`_. :Index terms: pair: protocol inheritance; design +:Readership: MPS developers Introduction ------------ _`.intro`: This document explains the design of the support for class -inheritance in MPS. It is not yet complete. It describes support for -single inheritance of classes. Future extensions will describe -multiple inheritance and the relationship between instances and -classes. +inheritance in MPS. _`.readership`: This document is intended for any MPS developer. @@ -71,202 +69,309 @@ several classes which do not themselves support identical protocols. Overview -------- -``typedef struct ProtocolClassStruct *ProtocolClass`` +_`.overview.inst`: The key concept in the design is the relationship +between an "instance" and its "class". Every structure that +participates in the protocol system begins with an ``InstStruct`` +structure that contains a pointer to an ``InstClassStruct`` that +describes it, like this:: -_`.overview.root`: We start with the root of all conformant class -hierarchies, which is called ``ProtocolClass``. This is an "abstract" -class (that is, it has no direct instances, but it is intended to have -subclasses). To use Dylan terminology, instances of its subclasses are -"general" instances of ProtocolClass. They look like this:: + instance class - Instance Object Class Object + .----------. .----------. + | class |----->| class | + ------------ ------------ + | ... | | sig | + ------------ ------------ + | ... | | name | + ------------ ------------ + | ... | |superclass| + ------------ ------------ + | | | ... | - -------------------- -------------------- - | sig | |-------->| sig | - -------------------- | -------------------- - | class |----| | superclass | - -------------------- -------------------- - | ... | | coerceInst | - -------------------- -------------------- - | ... | | coerceClass | - -------------------- -------------------- - | | | ... | +_`.overview.prefix`: We make use of the fact that we can cast between +structures with common prefixes, or between structures and their first +members, to provide dynamic typing and subtyping (see +[Kernighan_1988]_, A.8.3). -_`.overview.inherit`: Classes inherit the protocols supported by their -superclasses. By default they have the same methods as the class(es) -from which they inherit. +_`.overview.method`: The ``InstClassStruct`` it itself at the start of +a class structure contains pointers to functions that can be called to +manipulate the instance as an abstract data type. We refer to these +functions as "methods" to distinguish them from functions not involved +in the object-oriented protocol. The macro ``Method`` is provided for +calling methods. + +_`.overview.subclass`: An instance structure can be extended by using +it as the first field of another structure, and by overriding its +class pointer with a pointer to a "subclass" that provides different +behavior. + +_`.overview.inherit`: Classes inherit the methods from their +superclasses when they are initialized, so by default they have the +same methods as the class from which they inherit. Methods on the +superclass can be re-used, providing polymorphism. _`.overview.inherit.specialize`: Classes may specialize the behaviour of their superclass. They do this by by overriding methods or other fields in the class object. +_`.overview.mixin`: Groups of related overrides are provided by +"mixins", and this provides a limited form of multiple inheritance. + _`.overview.extend`: Classes may extend the protocols supported by their superclasses by adding new fields for methods or other data. +Extending a class creates a new kind of class. -_`.overview.sig.inherit`: Classes will contain (possibly several) -signatures. Classes must not specialize (override) the signatures they -inherit from their superclasses. +_`.overview.kind`: Classes are themselves instance objects, and have +classes of their own. A class of a class is referred to as a "kind", +but is not otherwise special. Classes which share the same set of +methods (or other class fields) are instances of the same kind. If a +class is extended, it becomes a member of a different kind. Kinds +allow subtype checking to be applied to classes as well as instances, +to determine whether methods are available. :: -_`.overview.sig.extend`: If a class definition extends a protocol, it -is normal policy for the class definition to include a new signature -as the last field in the class object. + instance class kind + (e.g. CBS) (e.g. CBSClass) (e.g. LandClassClass) + .----------. .----------. .----------. + | class |----->| class |----->| class |-->InstClassClass + ------------ ------------ ------------ + | ... | | sig | | sig | + ------------ ------------ ------------ + | ... | | name | | name | + ------------ ------------ ------------ + | ... | |superclass|-. |superclass|-->InstClassClass + ------------ ------------ | ------------ + | | | ... | | | ... | + | + | + LandClass<-' -_`.overview.coerce-class`: Each class contains a ``coerceClass`` -field. This contains a method which can find the part of the class -object which implements the protocols of a supplied superclass -argument (if, indeed, the argument *is* a superclass). This function -may be used for testing subclass/superclass relationships, and it also -provides support for multiple inheritance. -_`.overview.coerce-inst`: Each class contains a ``coerceInst`` field. -This contains a method which can find the part of an instance object -which contains the instance slots of a supplied superclass argument -(if, indeed, the argument *is* a superclass). This function may be -used for testing whether an object is an instance of a given class, -and it also provides support for multiple inheritance. +_`.overview.sig.inherit`: Instances (and therefore classes) will +contain signatures. Classes must not specialize (override) the +signatures they inherit from their superclasses, as they are used to +check the actual type (not sub- or supertype) of the object they're +in. + +_`.overview.sig.extend`: When extending an instance or class, it is +normal policy for the new structure to include a new signature as the +last field. _`.overview.superclass`: Each class contains a ``superclass`` field. -This enables classes to call "next-method", as well as enabling the -coercion functions. +This enables classes to call "next-method". _`.overview.next-method`: A specialized method in a class can make use -of an overridden method from a superclass by accessing the method from -the appropriate field in the superclass object and calling it. The -superclass may be accessed indirectly from the class's "Ensure" -function when it is statically known (see `.overview.access`_). This -permits "next-method" calls, and is fully scalable in that it allows -arbitrary length method chains. The ``SUPERCLASS()`` macro helps with -this (see `.int.static-superclass`_). +of an overridden method from a superclass using the ``NextMethod`` +macro, statically naming the superclass. -_`.overview.next-method.naive`: In some cases it is necessary to write -a method which is designed to specialize an inherited method, needs to -call the next-method, and yet the implementation doesn't have static -knowledge of the superclass. This might happen because the specialized -method is designed to be reusable by many class definitions. The -specialized method can usually locate the class object from one of the -parameters passed to the method. It can then access the superclass -through the ``superclass`` field of the class, and hence call the next -method. This technique has some limitations and doesn't support longer -method chains. It is also dependent on none of the class definitions -which use the method having any subclasses. +_`.overview.next-method.dynamic`: It is possible to write a method +which does not statically know its superclass, and call the next +method by extracting a class from one of its arguments using +``ClassOfPoly`` and finding the superclass using ``SuperclassPoly``. +Debug pool mixins do this. However, this is not fully general, and +combining such methods is likely to cause infinite recursion. Take +care! _`.overview.access`: Classes must be initialized by calls to -functions, since it is these function calls which copy properties from -superclasses. Each class must provide an "Ensure" function, which -returns the canonical copy of the class. The canonical copy may reside -in static storage, but no MPS code may refer to that static storage by -name. +functions, since there is no way to express overrides statically in +C89. ``DEFINE_CLASS`` defines an "ensure" function that initializes +and returns the canonical copy of the class. The canonical copy may +reside in static storage, but no MPS code may refer to that static +storage by name. + +_`.overview.init`: In addition to the "ensure" function, each class +must provide an "init" function, which initialises its argument as a +fresh copy of the class. This allows subclasses to derive their +methods and other fields from superclasses. _`.overview.naming`: There are some strict naming conventions which must be followed when defining and using classes. The use is obligatory because it is assumed by the macros which support the -definition and inheritance mechanism. For every class ``SomeClass``, -we insist upon the following naming conventions:- +definition and inheritance mechanism. For every kind ``Foo``, +we insist upon the following naming conventions: -* ``SomeClassStruct`` +* ``Foo`` names a type that points to a ``FooStruct``. - names the type of the structure for the protocol class. This might - be a ``typedef`` which aliases the type to the type of the - superclass, but if the class has extended the protocols of the - superclass the it will be a type which contains the new class - fields. +* ``FooStruct`` is the type of the instance structure, the first field + of which is the structure it inherits from (ultimately an + ``InstStruct``). -* ``SomeClass`` - - names the type ``*SomeClassStruct``. This might be a ``typedef`` - which aliases the type to the type of the superclass, but if the - class has extended the protocols of the superclass then it will be - a type which contains the new class fields. - -* ``EnsureSomeClass()`` - - names the function that returns the initialized class object. +* ``FooClass`` names the type that points to a ``FooClassStruct``. +* ``FooClassStruct`` names the structure for the class pointed to by + ``FooStruct``, containing the methods that operate on ``Foo``. Interface --------- + +Class declaration +................. + +``DECLARE_CLASS(kind, className)`` + +_`.if.declare-class`: Class declaration is performed by the macro +``DECLARE_CLASS``, which declares the existence of the class +definition elsewhere. It is intended for use in headers. + + Class definition ................ -``DEFINE_CLASS(className, var)`` +``DEFINE_CLASS(kind, className, var)`` -_`.int.define-class`: Class definition is performed by the macro -``DEFINE_CLASS()``. A call to the macro must be followed by a body of -initialization code in braces ``{}``. The parameter ``className`` is -used to name the class being defined. The parameter ``var`` is used to -name a local variable of type ``className``, which is defined by the -macro; it refers to the canonical storage for the class being defined. -This variable may be used in the initialization code. (The macro -doesn't just pick a name implicitly because of the danger of a name -clash with other names used by the programmer). A call to -``DEFINE_CLASS(SomeClass, var)`` defines the ``EnsureSomeClass()`` -function, defines some static storage for the canonical class object, -and defines some other things to ensure the class gets initialized -exactly once. +_`.if.define-class`: Class definition is performed by the macro +``DEFINE_CLASS``. A call to the macro must be followed by a function +body of initialization code. The parameter ``className`` is used to +name the class being defined. The parameter ``var`` is used to name a +local variable of type of classes of kind ``kind``, which is defined +by the macro; it refers to the canonical storage for the class being +defined. This variable may be used in the initialization code. (The +macro doesn't just pick a name implicitly because of the danger of a +name clash with other names used by the programmer). A call to the +macro defines the ensure function for the class along with some static +storage for the canonical class object, and some other things to +ensure the class gets initialized at most once. -``DEFINE_ALIAS_CLASS(className, typeName, var)`` -_`.int.define-alias-class`: A convenience macro -``DEFINE_ALIAS_CLASS()`` is provided which both performs the class -definition and defines the types ``SomeClass`` and ``SomeClass -struct`` as aliases for some other class types. This is particularly -useful for classes which simply inherit, and don't extend protocols. -The macro call ``DEFINE_ALIAS_CLASS(className, superName, var)`` is -exactly equivalent to the following:: +Class access +............ - typedef superName className; - typedef superNameStruct classNameStruct; - DEFINE_CLASS(className, var) +``CLASS(className)`` -_`.int.define-special`: If classes are particularly likely to be -subclassed without extension, the class implementor may choose to -provide a convenience macro which expands into ``DEFINE_ALIAS_CLASS()`` -with an appropriate name for the superclass. For example, there might -be a macro for defining pool classes such that the macro call -``DEFINE_POOL_CLASS(className, var)`` is exactly equivalent to the -macro call ``DEFINE_ALIAS_CLASS(className, PoolClass, var)``. It may -also be convenient to define a static superclass accessor macro at the -same time (see `.int.static-superclass.special`_). +_`.if.class`: To get the canonical class object, use the ``CLASS`` +macro, e.g. ``CLASS(Land)``. Single inheritance .................. -``INHERIT_CLASS(thisClassCoerced, parentName)`` +``INHERIT_CLASS(this, className, parentName)`` -_`.int.inheritance`: Class inheritance details must be provided in the -class initialization code (see `.int.define-class`_). Inheritance is -performed by the macro ``INHERIT_CLASS()``. A call to this macro will +_`.if.inheritance`: Class inheritance details must be provided in the +class initialization code (see `.if.define-class`_). Inheritance is +performed by the macro ``INHERIT_CLASS``. A call to this macro will make the class being defined a direct subclass of ``parentClassName`` -by ensuring that all the fields of the parent class are copied into -``thisClass``, and setting the superclass field of ``thisClass`` to be -the parent class object. The parameter ``thisClassCoerced`` must be of -type ``parentClassName``. If the class definition defines an alias -class (see `.int.define-alias-class`_), then the variable named as the -second parameter to ``DEFINE_CLASS()`` will be appropriate to pass to -``INHERIT_CLASS()``. +by ensuring that all the fields of the embedded parent class (pointed +to by the ``this`` argument) are initialized as the parent class, and +setting the superclass field of ``this`` to be the canonical parent +class object. The parameter ``this`` must be the the same kind as +``parentClassName``. Specialization .............. -_`.int.specialize`: Class specialization details must be given +_`.if.specialize`: Fields in the class structure must be assigned explicitly in the class initialization code (see -`.int.define-class`_). This must happen *after* the inheritance -details are given (see `.int.inheritance`_). +`.if.define-class`_). This must happen *after* inheritance details +are given (see `.if.inheritance`_), so that overrrides work. Extension ......... -_`.int.extend`: To extend the protocol when defining a new class, a +_`.if.extend`: To extend the protocol when defining a new class, a new type must be defined for the class structure. This must embed the structure for the primarily inherited class as the first field of the -structure. Class extension details must be given explicitly in the -class initialization code (see `.int.define-class`_). This must happen -*after* the inheritance details are given (see `.int.inheritance`_). +structure. Extension fields in the class structure must be assigned +explicitly in the class initialization code (see +`.if.define-class`_). This should be done *after* the inheritance +details are given for consistency with `.if.inheritance`_. This is, +in fact, how all the useful classes extend ``Inst``. + +_`.if.extend.kind`: In addition, a class must be defined for the new +kind of class. This is just an unspecialized subclass of the kind of +the class being specialized by the extension. For example:: + + typedef struct LandClassStruct { + InstClassStruct instClass; /* inherited class */ + LandInsertMethod insert; + ... + } LandClassStruct; + + DEFINE_CLASS(Inst, LandClass, class) + { + INHERIT_CLASS(class, LandClass, InstClass); + } + + DEFINE_CLASS(Land, Land, class) + { + INHERIT_CLASS(&class->instClass, Land, Inst); + class->insert = landInsert; + ... + } + + +Methods +....... + +``Method(kind, inst, meth)`` + +_`.if.method`: To call a method on an instance of a class, use the +``Method`` macro to retrieve the method. This macro may assert if the +class is not of the kind requested. For example, to call the +``insert`` method on ``land``:: + + res = Method(Land, land, insert)(rangeReturn, land, range); + + +``NextMethod(kind, className, meth)`` + +_`.if.next-method`: To call a method from a superclass of a class, +use the ``NextMethod`` macro to retrieve the method. This macro may +assert if the superclass is not of the kind requested. For example, +the function to split AMS segments wants to split the segments they +are based on, so does:: + + res = NextMethod(Seg, AMSSeg, split)(seg, segHi, base, mid, limit); + + +Conversion +.......... + + +``IsA(className, inst)`` + +_`if.isa`: Returns non-zero iff the class of ``inst`` is a member of +the class or any of its subclasses. + + +``MustBeA(className, inst)`` + +_`.if.must-be-a`: To convert the C type of an instance to that of a +compatible class (the class of the actual object or any superclass), +use the ``MustBeA`` macro. In hot varieties this macro performs a +fast dynamic type check and will assert if the class is not +compatible. It is like C++ "dynamic_cast" with an assert. In cool +varieties, the class check method is called on the object. For +example, in a specialized Land method in the CBS class:: + + static Res cbsInsert(Range rangeReturn, Land land, Range range) + { + CBS cbs = MustBeA(CBS, land); + ... + + +``MustBeA_CRITICAL(className, inst)`` + +_`.if.must-be-a.critical`: When the cost of a type check is too +expensive in hot varieties, use ``MustBeA_CRITICAL`` in place of +``MustBeA``. This only performs the check in cool varieties. Compare +with ``AVER_CRITICAL``. + + +``CouldBeA(className, inst)`` + +_`.if.could-be-a`: To make an unsafe conversion equivalent to +``MustBeA``, use the ``CouldBeA`` macro. This is in effect a simple +pointer cast, but it expresses the intention of class compatibility in +the source code. It is mainly intended for use when initializing an +object, when a class compatibility check would fail, when checking an +object, or in debugging code such as describe methods, where asserting +is inappropriate. It is intended to be equivalent to the C++ +``static_cast``, although since this is C there is no actual static +checking, so in fact it's more like ``reinterpret_cast``. Introspection @@ -275,11 +380,11 @@ Introspection _`.introspect.c-lang`: The design includes a number of introspection functions for dynamically examining class relationships. These functions are polymorphic and accept arbitrary subclasses of -``ProtocolClass``. C doesn't support such polymorphism. So although -these have the semantics of functions (and could be implemented as -functions in another language with compatible calling conventions) -they are actually implemented as macros. The macros are named as -method-style macros despite the fact that this arguably contravenes +``InstClass``. C doesn't support such polymorphism. So although these +have the semantics of functions (and could be implemented as functions +in another language with compatible calling conventions) they are +actually implemented as macros. The macros are named as function-style +macros despite the fact that this arguably contravenes guide.impl.c.macro.method. The justification for this is that this design is intended to promote the use of polymorphism, and it breaks the abstraction for the users to need to be aware of what can and @@ -287,86 +392,56 @@ can't be expressed directly in C function syntax. These functions all have names ending in ``Poly`` to identify them as polymorphic functions. -``ProtocolClassSuperclassPoly(class)`` -_`.int.superclass`: An introspection function which returns the direct -superclass of class object ``class``. +``SuperclassPoly(kind, class)`` -``SUPERCLASS(className)`` +_`.if.superclass-poly`: An introspection function which returns the +direct superclass of class object ``class`` as a class of kind +``kind``. This may assert if the superclass is not (a subtype of) the +kind requested. -_`.int.static-superclass`: An introspection macro which returns the -direct superclass given a class name, which must (obviously) be -statically known. The macro expands into a call to the ensure function -for the class name, so this must be in scope (which may require a -forward declaration). The macro is useful for next-method calls (see -`.overview.next-method`_). The superclass is returned with type -``ProtocolClass`` so it may be necessary to cast it to the type for -the appropriate subclass. -_`.int.static-superclass.special`: Implementors of classes which are -designed to be subclassed without extension may choose to provide a -convenience macro which expands into a call to ``SUPERCLASS`` along -with a type cast. For example, there might be a macro for finding pool -superclasses such that the macro call ``POOL_SUPERCLASS(className)`` -is exactly equivalent to ``(PoolClass)SUPERCLASS(className)``. It's -convenient to define these macros alongside the convenience class -definition macro (see `.int.define-special`_). +``ClassOfPoly(kind, inst)`` -``ClassOfPoly(inst)`` +_`.if.class-of-poly`: An introspection function which returns the +class of which ``inst`` is a direct instance, as a class of kind +``kind``. This may assert if the class is not (a subtype of) the kind +requested. -_`.int.class`: An introspection function which returns the class of -which ``inst`` is a direct instance. -``IsSubclassPoly(sub, super)`` +``SetClassOfPoly(inst, class)`` -_`.int.subclass`: An introspection function which returns a ``Bool`` +_`.if.set-class-of-poly`: An initialization function that sets the +class of ``inst`` to be ``class``. This is intended only for use in +initialization functions, to specialize the instance once its fields +have been initialized. Each Init function should call its superclass +init, finally reaching InstInit, and then, once it has set up its +fields, use SetClassOfPoly to set the class and check the instance +with its check method. Compare with `design.mps.sig`_. + +.. _`design.mps.sig`: sig + +``IsSubclass(sub, super)`` + +_`.if.is-subclass`: An introspection function which returns a ``Bool`` indicating whether ``sub`` is a subclass of ``super``. That is, it is a predicate for testing subclass relationships. -Multiple inheritance -.................... - -_`.int.mult-inherit`: Multiple inheritance involves an extension of -the protocol (see `.int.extend`_) and also multiple uses of the single -inheritance mechanism (see `.int.inheritance`_). It also requires -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`_. - -.. _mail.tony.1998-10-06.11-03: https://info.ravenbrook.com/project/mps/mail/1998/10/06/11-03/0.txt - - Protocol guidelines ................... -_`.guide.fail`: When designing an extensible function which might -fail, the design must permit the correct implementation of the -failure-case code. Typically, a failure might occur in any method in -the chain. Each method is responsible for correctly propagating -failure information supplied by superclass methods and for managing -it's own failures. - -_`.guide.fail.before-next`: Dealing with a failure which is detected -before any next-method call is made is similar to a fail case in any -non-extensible function. See `.example.fail`_ below. - -_`.guide.fail.during-next`: Dealing with a failure returned from a -next-method call is also similar to a fail case in any non-extensible -function. See `.example.fail`_ below. - -_`.guide.fail.after-next`: Dealing with a failure which is detected -after the next methods have been successfully invoked is more complex. -If this scenario is possible, the design must include an -"anti-function", and each class must ensure that it provides a method -for the anti-method which will clean up any resources which are -claimed after a successful invocation of the main method for that -class. Typically the anti-function would exist anyway for clients of -the protocol (for example, "finish" is an anti-function for "init"). -The effect of the next-method call can then be cleaned up by calling -the anti-method for the superclass. See `.example.fail`_ below. +_`.guide.fail`: When designing an extensible method which might fail, +the design must permit the correct implementation of the failure-case +code. Typically, a failure might occur in any method in the chain. +Each method is responsible for correctly propagating failure +information supplied by superclass methods and for managing it's own +failures. This is not really different from the general MPS +convention for unwinding on error paths. It implies that the design +of a class must include an anti-method for each method that changes +the state of an instance (e.g. by allocating memory) to allow the +state to be reverted in case of a failure. See `.example.fail`_ +below. Example @@ -374,158 +449,178 @@ Example _`.example.inheritance`: The following example class definition shows both inheritance and specialization. It shows the definition of the -class ``EPDRPoolClass``, which inherits from ``EPDLPoolClass`` and has -specialized values of the ``name``, ``init``, and ``alloc`` fields. -The type ``EPDLPoolClass`` is an alias for ``PoolClass``. :: +class ``RankBuf``, which inherits from ``SegBuf`` of kind ``Seg`` +and has specialized ``varargs`` and ``init`` method. :: - typedef EPDLPoolClass EPDRPoolClass; - typedef EPDLPoolClassStruct EPDRPoolClassStruct; - - DEFINE_CLASS(EPDRPoolClass, this) + DEFINE_CLASS(Buffer, RankBuf, class) { - INHERIT_CLASS(this, EPDLPoolClass); - this->name = "EPDR"; - this->init = EPDRInit; - this->alloc = EPDRAlloc; + INHERIT_CLASS(class, RankBuf, SegBuf); + class->varargs = rankBufVarargs; + class->init = rankBufInit; } _`.example.extension`: The following (hypothetical) example class definition shows inheritance, specialization and also extension. It -shows the definition of the class ``EPDLDebugPoolClass``, which -inherits from ``EPDLPoolClass``, but also implements a method for +shows the definition of the class ``EPDLDebugPool``, which inherits +from ``EPDLPool`` of kind ``Pool``, but also implements a method for checking properties of the pool. :: typedef struct EPDLDebugPoolClassStruct { - EPDLPoolClassStruct epdl; - DebugPoolCheckMethod check; - Sig sig; + EPDLPoolClassStruct epdl; + DebugPoolCheckMethod check; + Sig sig; } EPDLDebugPoolClassStruct; typedef EPDLDebugPoolClassStruct *EPDLDebugPoolClass; - DEFINE_CLASS(EPDLDebugPoolClass, this) + DEFINE_CLASS(Inst, EPDLDebugPoolClass, class) { - EPDLPoolClass epdl = &this->epdl; - INHERIT_CLASS(epdl, EPDLPoolClass); - epdl->name = "EPDLDBG"; - this->check = EPDLDebugCheck; - this->sig = EPDLDebugSig; + INHERIT_CLASS(class, EPDLPoolClass, InstClass); + } + + DEFINE_CLASS(EPDLDebugPool, EPDLDebugPool, class) + { + INHERIT_CLASS(&class->epdl, EPDLDebugPool, EPDLPoolClass); + class->check = EPDLDebugCheck; + class->sig = EPDLDebugSig; } _`.example.fail`: The following example shows the implementation of failure-case code for an "init" method, making use of the "finish" -anti-method:: +anti-method to clean-up a subsequent failure. :: - static Res mySegInit(Seg seg, Pool pool, Addr base, Size size, - Bool reservoirPermit, va_list args) + static Res AMSSegInit(Seg seg, Pool pool, + Addr base, Size size, + ArgList args) { - SegClass super; - MYSeg myseg; - OBJ1 obj1; - Res res; - Arena arena; + AMS ams = MustBeA(AMSPool, pool); + Arena arena = PoolArena(pool); + AMSSeg amsseg; + Res res; - AVERT(Seg, seg); - myseg = SegMYSeg(seg); - AVERT(Pool, pool); - arena = PoolArena(pool); + /* Initialize the superclass fields first via next-method call */ + res = NextMethod(Seg, AMSSeg, init)(seg, pool, base, size, args); + if (res != ResOK) + goto failNextMethod; + amsseg = CouldBeA(AMSSeg, seg); - /* Ensure the pool is ready for the segment */ - res = myNoteSeg(pool, seg); - if(res != ResOK) - goto failNoteSeg; + amsseg->grains = size >> ams->grainShift; + amsseg->freeGrains = amsseg->grains; + amsseg->oldGrains = (Count)0; + amsseg->newGrains = (Count)0; + amsseg->marksChanged = FALSE; /* */ + amsseg->ambiguousFixes = FALSE; - /* Initialize the superclass fields first via next-method call */ - super = (SegClass)SUPERCLASS(MYSegClass); - res = super->init(seg, pool, base, size, reservoirPermit, args); - if(res != ResOK) - goto failNextMethods; + res = amsCreateTables(ams, &amsseg->allocTable, + &amsseg->nongreyTable, &amsseg->nonwhiteTable, + arena, amsseg->grains); + if (res != ResOK) + goto failCreateTables; - /* Create an object after the next-method call */ - res = ControlAlloc(&obj1, arena, sizeof(OBJ1Struct), reservoirPermit); - if(res != ResOK) - goto failObj1; + /* start off using firstFree, see */ + amsseg->allocTableInUse = FALSE; + amsseg->firstFree = 0; + amsseg->colourTablesInUse = FALSE; - myseg->obj1 = obj1 - return ResOK; + amsseg->ams = ams; + RingInit(&amsseg->segRing); + RingAppend((ams->allocRing)(ams, SegRankSet(seg), size), + &amsseg->segRing); - failObj1: - /* call the anti-method for the superclass */ - super->finish(seg); - failNextMethods: - /* reverse the effect of myNoteSeg */ - myUnnoteSeg(pool, seg); - failNoteSeg: - return res; + SetClassOfPoly(seg, CLASS(AMSSeg)); + amsseg->sig = AMSSegSig; + AVERC(AMSSeg, amsseg); + + return ResOK; + + failCreateTables: + NextMethod(Seg, AMSSeg, finish)(seg); + failNextMethod: + AVER(res != ResOK); + return res; } Implementation -------------- +_`.impl.define-class.lock`: The ``DEFINE_CLASS`` macro ensures that +each class is initialized at most once (even in multi-threaded +programs) by claiming the global recursive lock (see design.mps.thread-safety.arch.global.recursive_). + +.. _design.mps.thread-safety.arch.global.recursive: thread-safety#arch-global-recursive + _`.impl.derived-names`: The ``DEFINE_CLASS()`` macro derives some additional names from the class name as part of it's implementation. -These should not appear in the source code - but it may be useful to +These should not appear in the source code, but it may be useful to know about this for debugging purposes. For each class definition for -class ``SomeClass``, the macro defines the following: +class ``SomeClass`` of kind ``SomeKind``, the macro defines the +following: -* ``extern SomeClass EnsureSomeClass(void);`` +* ``extern SomeKind SomeClassGet(void);`` - The class accessor function. See `.overview.naming`_. + The class ensure function. See `.overview.naming`_. This function + handles local static storage for the canonical class object and a + guardian to ensure the storage is initialized at most once. This + function is invoked by the ``CLASS`` macro (`.if.class`_). -* ``static Bool protocolSomeClassGuardian;`` +* ``static void SomeClassInit(SomeKind);`` - A Boolean which indicates whether the class has been initialized - yet. - -* ``static void protocolEnsureSomeClass(SomeClass);`` - - A function called by ``EnsureSomeClass()``. All the class + A function called by ``SomeClassGet()``. All the class initialization code is actually in this function. -* ``static SomeClassStruct protocolSomeClassStruct;`` +_`.impl.subclass`: The subclass test `.if.subclass`_ is implemented +using an array of superclasses [Cohen_1991]_ giving a fast +constant-time test. (RB_ tried an approach using prime factors +[Gibbs_2004]_ but found that they overflowed in 32-bits too easily to +be useful.) Each class is assigned a "level" which is the distance +from the root of the class hierarchy. The ``InstClass`` structure +contains an array of class ids indexed by level, representing the +inheritance of this class. A class is a subclass of another if and +only if the superclass id is present in the array at the superclass +level. The level is statically defined using enum constants, and the +id is the address of the canonical class object, so the test is fast +and simple. - Static storage for the canonical class object. - -_`.impl.init-once`: Class objects only behave according to their -definition after they have been initialized, and class protocols may -not be used before initialization has happened. The only code which is -allowed to see a class object in a partially initialized state is the -initialization code itself -- and this must take care not to pass the -object to any other code which might assume it is initialized. Once a -class has been initialized, the class might have a client. The class -must not be initialized again when this has happened, because the -state is not necessarily consistent in the middle of an initialization -function. The initialization state for each class is stored in a -Boolean "guardian" variable whose name is derived from the class name -(see `.impl.derived-names`_). This ensures the initialization happens -only once. The path through the ``EnsureSomeClass()`` function should be -very fast for the common case when this variable is ``TRUE``, and the -class has already been initialized, as the canonical static storage -can simply be returned in that case. However, when the value of the -guardian is ``FALSE``, the class is not initialized. In this case, a -call to ``EnsureSomeClass()`` must first execute the initialization code -and then set the guardian to ``TRUE``. However, this must happen -atomically (see `.impl.init-lock`_). - -_`.impl.init-lock`: There would be the possibility of a race condition -if ``EnsureSomeClass()`` were called concurrently on separate threads -before ``SomeClass`` has been initialized. The class must not be -initialized more than once, so the sequence test-guard, init-class, -set-guard must be run as a critical region. It's not sufficient to use -the arena lock to protect the critical region, because the class -object might be shared between multiple arenas. The ``DEFINE_CLASS()`` -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_. - -.. _design.mps.lock: lock +.. _RB: http://www.ravenbrook.com/consultants/rb/ -Document History ----------------- +Common instance methods +----------------------- + +_`.method`: These methods are available on all instances. + +``typedef void (*FinishMethod)(Inst inst)`` + +_`.method.finish`: The ``finish`` method should finish the instance +data structure (releasing any resources that were acquired by the +instance during its lifetime) and then call its superclass method via +the ``NextMethod()`` macro. + +``typedef Res (*DescribeMethod)(Inst inst, mps_lib_FILE *stream, Count depth)`` + +_`.method.describe`: The ``describe`` field should print out a +description of the instance to ``stream`` (by calling ``WriteF()``). + + +A. References +------------- + +.. [Cohen_1991] "Type-Extension Type Tests Can Be Performed In + Constant Time"; Norman H Cohen; IBM Thomas J Watson Research + Center; ACM Transactions on Programming Languages and Systems, + Vol. 13 No. 4, pp. 626-629; 1991-10. + +.. [Gibbs_2004] "Fast Dynamic Casting"; Michael Gibbs, Bjarne + Stroustrup; 2004; + . + +.. [Kernighan_1988] "The C Programming language 2nd Edition"; Brian W. + Kernighan, Dennis M. Ritchie; 1988. + + +B. Document History +------------------- - 1998-10-12 Tony Mann. Initial draft. @@ -533,15 +628,25 @@ Document History - 2013-04-14 GDR_ Converted to reStructuredText. -.. _RB: http://www.ravenbrook.com/consultants/rb/ +- 2016-04-07 RB_ Removing never-used multiple inheritance speculation. + +- 2016-04-08 RB_ Substantial reorgnisation. + +- 2016-04-13 RB_ Writing up overview of kinds, with explanation of + class extension. Writing up ``Method``, ``NextMethod``, + ``SetClassOfPoly``, ``MustBeA``, etc. and updating the descriptions + of some older interface. Updating the example. + +- 2016-04-19 RB_ Miscellaneous clean-up in response to review by GDR_. + .. _GDR: http://www.ravenbrook.com/consultants/gdr/ -Copyright and License ---------------------- +C. Copyright and License +------------------------ -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2013-2018 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 diff --git a/mps/design/protsu.txt b/mps/design/protsu.txt deleted file mode 100644 index e31edf3fe73..00000000000 --- a/mps/design/protsu.txt +++ /dev/null @@ -1,171 +0,0 @@ -.. mode: -*- rst -*- - -SunOS 4 protection module -========================= - -:Tag: design.mps.protsu -:Author: David Jones -:Date: 1997-03-20 -:Status: incomplete document -:Revision: $Id$ -:Copyright: See `Copyright and License`_. -:Index terms: - pair: SunOS 4; protection interface design - pair: SunOS 4 protection interface; design - - -.. warning:: - - As of 2013-05-26, the MPS is no longer supported on SunOS, so - this document is only of historical interest. - - -Introduction ------------- - -_`.readership`: Any MPS developer. - -_`.intro`: This is the design of the SunOS 4 implementation of the -protection module. It is intended to be used only in SunOS 4 (os.su). -It makes use of various services provided by SunOS 4. - - -Requirements ------------- - -_`.req.general`: Required to implement the general protection -interface defined in design.mps.prot.if_. - -.. _design.mps.prot.if: prot#if - - -Overview --------- - -Uses ``mprotect()``. - - -Misc ----- - -_`.improve.sig-stack`: Currently we do not handle signals on a -separate signal stack. If we handled signals on our own stack then we -could guarantee not to run out of stack while we were handling the -signal. This would be useful (it may even be required). We would have -to use ``sigvec(2)`` rather than ``signal(3)`` (set the ``SV_ONSTACK`` -flag and use ``sigstack(2)``). This has drawbacks as the signal stack -is not grown automatically, so we would have to to frig the stacks -back if we wanted to pass on the signal to some other handler as that -handler may require arbitrary amounts of stack. - -_`.improve.sigvec`: Note 1 of ``ProtSetup()`` notes that we can't -honour the ``sigvec(2)`` entries of the next handler in the chain. -What if when we want to pass on the signal instead of calling the -handler we call ``sigvec()`` with the old entry and use kill to send -the signal to ourselves and then restore our handler using sigvec -again. - - -Data structures ---------------- - -_`.data.signext`: This is static. Because that is the only -communications channel available to signal handlers. [write a little -more here] - - -Functions ---------- - -_`.fun.setup`: ``ProtSetup()``. The setup involves installing a signal -handler for the signal ``SIGSEGV`` to catch and handle protection -faults (this handler is the function ``sigHandle()``). The previous -handler is recorded (in the variable ``sigNext``, see -`.data.signext`_) so that it can be reached from ``sigHandle()`` if it -fails to handle the fault. - -The problem with this approach is that we can't honor the wishes of the -``sigvec(2)`` entry for the previous handler (in terms of masks in particular). - -Obviously it would be okay to always chain the previous signal handler -onto ``sigNext``, however in the case where the previous handler is -the one we've just installed (that is, ``sigHandle``) then it is not -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 -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 -not read accesses) then write accesses are forbidden 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. This is because we should always call the function -with valid arguments (aligned, references to mapped pages, and with an -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. - - -Document History ----------------- - -- 1997-03-20 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/mps/design/pthreadext.txt b/mps/design/pthreadext.txt index 18b7b2d9d9f..0b14299dc18 100644 --- a/mps/design/pthreadext.txt +++ b/mps/design/pthreadext.txt @@ -135,8 +135,8 @@ Interface _`.if.pthreadext.abstract`: A thread is represented by the abstract type ``PThreadext``. A ``PThreadext`` object corresponds directly with -a PThread (of type ``pthread_t``). There may be more than one -``PThreadext`` object for the same PThread. +a thread (of type ``pthread_t``). There may be more than one +``PThreadext`` object for the same thread. _`.if.pthreadext.structure`: The structure definition of ``PThreadext`` (``PThreadextStruct``) is exposed by the interface so @@ -163,16 +163,16 @@ _`.if.suspend`: Suspends a ``PThreadext`` object (puts it into a suspended state). Meets `.req.suspend`_. The object must not already be in a suspended state. If the function returns ``ResOK``, the context of the thread is returned in contextReturn, and the -corresponding PThread will not make any progress until it is resumed: +corresponding thread will not make any progress until it is resumed. ``Res PThreadextResume(PThreadext pthreadext)`` -_`.if.resume`: Resumes a ``PThreadext`` object. Meets -`.req.resume`_. The object must already be in a suspended state. -Puts the object into a non-suspended state. Permits the corresponding -PThread to make progress again, (although that might not happen -immediately if there is another suspended ``PThreadext`` object -corresponding to the same thread): +_`.if.resume`: Resumes a ``PThreadext`` object. Meets `.req.resume`_. +The object must already be in a suspended state. Puts the object into +a non-suspended state. Permits the corresponding thread to make +progress again, although that might not happen immediately if there is +another suspended ``PThreadext`` object corresponding to the same +thread. ``void PThreadextFinish(PThreadext pthreadext)`` @@ -189,9 +189,9 @@ _`.impl.pthreadext`: The structure definition for a ``PThreadext`` object is:: struct PThreadextStruct { - Sig sig; /* design.mps.sig */ + Sig sig; /* */ pthread_t id; /* Thread ID */ - struct sigcontext *suspendedScp; /* sigcontext if suspended */ + MutatorContext context; /* context if suspended */ RingStruct threadRing; /* ring of suspended threads */ RingStruct idRing; /* duplicate suspensions for id */ }; @@ -199,7 +199,7 @@ object is:: _`.impl.field.id`: The ``id`` field shows which PThread the object corresponds to. -_`.impl.field.scp`: The ``suspendedScp`` field contains the context +_`.impl.field.context`: The ``context`` field contains the context when in a suspended state. Otherwise it is ``NULL``. _`.impl.field.threadring`: The ``threadRing`` field is used to chain @@ -208,29 +208,30 @@ the object onto the suspend ring when it is in the suspended state this ring is single. _`.impl.field.idring`: The ``idRing`` field is used to group the -object with other objects corresponding to the same PThread (same +object with other objects corresponding to the same thread (same ``id`` field) when they are in the suspended state. When not in a suspended state, or when this is the only ``PThreadext`` object with this ``id`` in the suspended state, this ring is single. -_`.impl.global.suspend-ring`: The module maintains a global -suspend-ring -- a ring of ``PThreadext`` objects which are in a +_`.impl.global.suspend-ring`: The module maintains a global varaible +``suspendedRing``, a ring of ``PThreadext`` objects which are in a suspended state. This is primarily so that it's possible to determine whether a thread is curently suspended anyway because of another ``PThreadext`` object, when a suspend attempt is made. -_`.impl.global.victim`: The module maintains a global variable which -is used to indicate which ``PThreadext`` is the current victim during -suspend operations. This is used to communicate information between -the controlling thread and the thread being suspended (the victim). -The variable has value ``NULL`` at other times. +_`.impl.global.victim`: The module maintains a global variable +``suspendingVictim`` which is used to indicate which ``PThreadext`` is +the current victim during suspend operations. This is used to +communicate information between the controlling thread and the thread +being suspended (the victim). The variable has value ``NULL`` at other +times. _`.impl.static.mutex`: We use a lock (mutex) around the suspend and -resume operations. This protects the state data (the suspend-ring the -victim: see `.impl.global.suspend-ring`_ and `.impl.global.victim`_ -respectively). Since only one thread can be suspended at a time, -there's no possibility of two arenas suspending each other by -concurrently suspending each other's threads. +resume operations. This protects the state data (the suspend-ring and +the victim: see `.impl.global.suspend-ring`_ and +`.impl.global.victim`_ respectively). Since only one thread can be +suspended at a time, there's no possibility of two arenas suspending +each other by concurrently suspending each other's threads. _`.impl.static.semaphore`: We use a semaphore to synchronize between the controlling and victim threads during the suspend operation. See @@ -324,10 +325,17 @@ likely to be generated and/or handled by other parts of the application and so should not be used (for example, ``SIGSEGV``). Some implementations of PThreads use some signals for themselves, so they may not be used; for example, LinuxThreads uses ``SIGUSR1`` and -``SIGUSR2`` for its own purposes. The design abstractly names the -signals ``PTHREADEXT_SIGSUSPEND`` and ``PTHREAD_SIGRESUME``, so that -they may be easily mapped to appropriate real signal values. Candidate -choices are ``SIGXFSZ`` and ``SIGPWR``. +``SIGUSR2`` for its own purposes, and so do popular tools like +Valgrind that we would like to be compatible with the MPS. The design +therefore abstractly names the signals ``PTHREADEXT_SIGSUSPEND`` and +``PTHREAD_SIGRESUME``, so that they may be easily mapped to +appropriate real signal values. Candidate choices are ``SIGXFSZ`` and +``SIGXCPU``. + +_`.impl.signals.config`: The identity of the signals used to suspend +and resume threads can be configured at compilation time using the +preprocessor constants ``CONFIG_PTHREADEXT_SIGSUSPEND`` and +``CONFIG_PTHREADEXT_SIGRESUME`` respectively. Attachments @@ -368,8 +376,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2013-2018 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 diff --git a/mps/design/range.txt b/mps/design/range.txt index 9e6d4ce34a4..e7c8ab3390f 100644 --- a/mps/design/range.txt +++ b/mps/design/range.txt @@ -40,9 +40,10 @@ Interface ``typedef RangeStruct *Range`` -``Range`` is the type of a range. It is an alias for ``RangeStruct *``. ``RangeStruct`` is defined in the header so that it can be -inlined in client structures or allocated on the stack. Clients must -not depend on its implementation details. +``Range`` is the type of a range. It is an alias for +``RangeStruct *``. ``RangeStruct`` is defined in the header so that it +can be inlined in client structures or allocated on the stack. Clients +must not depend on its implementation details. ``void RangeInit(Range range, Addr base, Addr limit)`` @@ -78,6 +79,18 @@ there is a function too.) Return the limit of the range. (This is implemented as a macro, but there is a function too.) +``void RangeSetBase(Range range, Addr addr)`` + +Set the base of the range. ``addr`` must not be greater than the range +limit. To set them both at once, use ``RangeInit()``. (This is +implemented as a macro, but there is a function too.) + +``void RangeSetLimit(Range range, Addr addr)`` + +Set the limit of the range. ``addr`` must not be less than the range +base. To set the both at once, use ``RangeInit()``. (This is +implemented as a macro, but there's a function too.) + ``Size RangeSize(Range range)`` Return the size of the range. (This is implemented as a macro, but @@ -116,16 +129,18 @@ Document history ---------------- - 2013-05-21 GDR_ Created. -- 2014-01-15 GDR_ Added ``RangeContains``. +- 2014-01-15 GDR_ Added ``RangeContains()``. +- 2016-03-27 RB_ Addded ``RangeSetBase()`` and ``RangeSetLimit()``. .. _GDR: http://www.ravenbrook.com/consultants/gdr/ +.. _RB: http://www.ravenbrook.com/consultants/rb/ Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2013-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 diff --git a/mps/design/reservoir.txt b/mps/design/reservoir.txt deleted file mode 100644 index 1c9e8c6fe1e..00000000000 --- a/mps/design/reservoir.txt +++ /dev/null @@ -1,170 +0,0 @@ -.. mode: -*- rst -*- - -The low-memory reservoir -======================== - -:Tag: design.mps.reservoir -:Author: Tony Mann -:Date: 1998-07-30 -:Status: incomplete design -:Revision: $Id$ -:Copyright: See `Copyright and License`_. -:Index terms: pair: reservoir; design - - -Introduction ------------- - -_`.intro`: The low-memory reservoir provides client support for -implementing handlers for low-memory situations which allocate. The -reservoir is implemented inside the arena as a pool of unallocatable -segments. - - -Architecture ------------- - -``typedef struct ReservoirStruct *Reservoir`` - -_`.adt`: The reservoir interface looks (almost) like an abstract data -type of type ``Reservoir``. It's not quite abstract because the arena -embeds the structure of the reservoir (of type ``ReservoirStruct``) -into its own structure, for simplicity of initialization. - -_`.align`: The reservoir is implemented as a pool of available tracts, -along with a size and limit which must always be aligned to the arena -alignment. The size corresponds to the amount of memory currently -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 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 -case, the client won't even want an allowance to be made for wastage -in active buffers. - - -Implementation --------------- - -_`.interface`: The following functions comprise the interface to the -reservoir module: - -``Bool ReservoirCheck(Reservoir reservoir)`` - -_`.interface.check`: ``ReservoirCheck()`` checks the reservoir for -consistency. - -``Res ReservoirInit(Reservoir reservoir, Arena arena)`` - -_`.interface.init`: ``ReservoirInit()`` initializes the reservoir and -its associated pool, setting the size and limit to 0. - -``void ReservoirFinish (Reservoir reservoir)`` - -_`.interface.finish`: ``ReservoirFinish()`` de-initializes the reservoir -and its associated pool: - -``Size ReservoirLimit(Reservoir reservoir)`` - -_`.interface.limit`: ``ReservoirLimit()`` returns the limit of the -reservoir: - -``void ReservoirSetLimit(Reservoir reservoir, Size size)`` - -_`.interface.set-limit`: ``ReservoirSetLimit()`` sets the limit of the -reservoir, making an allowance for wastage in mutator buffers: - -``Size ReservoirAvailable(Reservoir reservoir)`` - -_`.interface.available`: ``ReservoirAvailable()`` returns the available -size of the reservoir: - -``Res ReservoirEnsureFull(Reservoir reservoir)`` - -_`.interface.ensure-full`: ``ReservoirEnsureFull()`` attempts to fill -the reservoir with memory from the arena, until it is full: - -``void ReservoirDeposit(Reservoir reservoir, Addr base, Size size)`` - -_`.interface.deposit`: ``ReservoirDeposit()`` attempts to fill the -reservoir with memory in the supplied range, until it is full. This is -called by the arena from ``ArenaFree()`` if the reservoir is not known -to be full. Any memory which is not added to the reservoir (because -the reservoir is full) is freed via the arena class's free method. - -``Res ReservoirWithdraw(Addr *baseReturn, Tract *baseTractReturn, Reservoir reservoir, Size size, Pool pool)`` - -_`.interface.withdraw`: ``ReservoirWithdraw()`` attempts to allocate -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 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 -belonging to the pool is linked onto a list. The head of the list is -in the ``Reservoir`` object. Links are stored in the ``TractP`` fields -of each tract object. - - -Document History ----------------- - -- 1998-07-30 Tony Mann. Incomplete design. - -- 2002-06-07 RB_ Converted from MMInfo database design document. - -- 2013-05-22 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/mps/design/ring.txt b/mps/design/ring.txt index c0f4d9adfd4..1935cd02b29 100644 --- a/mps/design/ring.txt +++ b/mps/design/ring.txt @@ -112,7 +112,7 @@ additionally checks that ``ring`` is a singleton (see _`.is.single`: Return ``TRUE`` if ``ring`` is a singleton (see `.def.singleton`_). -``Size RingLength(Ring ring)`` +``Count 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 diff --git a/mps/design/root.txt b/mps/design/root.txt index dd20868f3ed..11eb65565a6 100644 --- a/mps/design/root.txt +++ b/mps/design/root.txt @@ -97,8 +97,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +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 diff --git a/mps/design/scan.txt b/mps/design/scan.txt index 2b4c992d185..f5d25ae0d90 100644 --- a/mps/design/scan.txt +++ b/mps/design/scan.txt @@ -24,7 +24,7 @@ _`.summary.subset`: The summary of reference seens by scan There are two reasons that it is not an equality relation: -#. If the segment has had objects forwarded onto it then its summary +1. If the segment has had objects forwarded onto it then its summary will get unioned with the summary of the segment that the object was forwarded from. This may increase the summary. The forwarded object of course may have a smaller summary (if such a thing were @@ -32,13 +32,13 @@ There are two reasons that it is not an equality relation: reduce the summary. (The forwarding process may erroneously introduce zones into the destination's summary). -#. A write barrier hit will set the summary to ``RefSetUNIV``. +2. 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 design.mps.trace.fix.fixed.all_. +in ``TraceFix()``. See design.mps.trace.fix.fixed.all_. -.. _design.mps.trace.fix.fixed.all: trace#fix.fixed.all +.. _design.mps.trace.fix.fixed.all: trace#fix-fixed-all Partial scans @@ -77,12 +77,12 @@ of all scanned references in the segment. We don't know this accurately until we've scanned everything in the segment. So we add in the segment summary each time. -_`.clever-summary.scan.fix`: TraceScan also expects the scan state -fixed summary to include the post-scan summary of all references which -were white. Since we don't scan all white references, we need to add -in an approximation to the summary of all white references which we -didn't scan. This is the intersection of the segment summary and the -white summary. +_`.clever-summary.scan.fix`: ``traceScanSeg()`` also expects the scan +state fixed summary to include the post-scan summary of all references +which were white. Since we don't scan all white references, we need to +add in an approximation to the summary of all white references which +we didn't scan. This is the intersection of the segment summary and +the white summary. _`.clever-summary.wb`: If the cumulative summary is smaller than the mutator's summary, a write-barrier is needed to prevent the mutator diff --git a/mps/design/seg.txt b/mps/design/seg.txt index db797f233c0..17730807e42 100644 --- a/mps/design/seg.txt +++ b/mps/design/seg.txt @@ -51,9 +51,15 @@ 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. +_`.over.hierarchy.gcseg`: ``GCSeg`` is a subclass of ``Seg`` which +implements garbage collection, including buffering and the ability to +be linked onto the grey ring. It does not implement hardware barriers, +and so can only be used with software barriers, for example internally +in the MPS. + +_`.over.hierarchy.mutatorseg`: ``MutatorSeg`` is a subclass of +``GCSeg`` implementing hardware barriers. It is suitable for handing +out to the mutator. Data Structure @@ -161,7 +167,7 @@ _`.split-and-merge`: There is support for splitting and merging segments, to give pools the flexibility to rearrange their tracts among segments as they see fit. -``Res SegSplit(Seg *segLoReturn, Seg *segHiReturn, Seg seg, Addr at, Bool withReservoirPermit, ...)`` +``Res SegSplit(Seg *segLoReturn, Seg *segHiReturn, Seg seg, Addr at)`` _`.split`: If successful, segment ``seg`` is split at address ``at``, yielding two segments which are returned in segLoReturn and @@ -190,7 +196,7 @@ _`.split.state`: Except as noted above, the segments returned have the same properties as ``seg``. That is, their colour, summary, rankset, nailedness etc. are set to the values of ``seg``. -``Res SegMerge(Seg *mergedSegReturn, Seg segLo, Seg segHi, Bool withReservoirPermit, ...)`` +``Res SegMerge(Seg *mergedSegReturn, Seg segLo, Seg segHi)`` _`.merge`: If successful, segments ``segLo`` and ``segHi`` are merged together, yielding a segment which is returned in mergedSegReturn. @@ -207,18 +213,16 @@ before calling ``SegMerge()``: represented if this is not so. - _`.merge.inv.buffer`: One or other of ``segLo`` and ``segHi`` may - attached to a buffer, but not both. Justification: the segment + be attached to a buffer, but not both. Justification: the segment module does not support attachment of a single seg to 2 buffers. - _`.merge.inv.similar`: ``segLo`` and ``segHi`` must be sufficiently similar. Two segments are sufficiently similar if they have identical values for each of the following fields: ``class``, - ``sm``, ``grey``, ``white``, ``nailed``, ``rankSet``. Justification: - there is no single choice of behaviour for cases where these fields - are not identical. The pool class must make it's own choices about - this if it wishes to permit more flexible merging. If so, it should - be a simple matter for the pool to arrange for the segments to look - sufficiently similar before calling ``SegMerge()``. + ``grey``, ``white``, ``nailed``, ``rankSet``. Justification: There + has yet to be a need to implement default behaviour for these + cases. Pool classes should arrange for these values to be the same + before calling ``SegMerge()``. _`.merge.state`: The merged segment will share the same state as ``segLo`` and ``segHi`` for those fields which are identical (see @@ -229,10 +233,157 @@ of ``segLo`` and ``segHi``. Extensibility ------------- +Allocation +.......... + +``typedef Bool (*SegBufferFillMethod)(Addr *baseReturn, Addr *limitReturn, Seg seg, Size size, RankSet rankSet)`` + +_`.method.buffer-fill`: Allocate a block in the segment, of at least +``size`` bytes, with the given set of ranks. If successful, update +``*baseReturn`` and ``*limitReturn`` to the block and return ``TRUE``. +Otherwise, return ``FALSE``. The allocated block must be accounted as +buffered (see design.mps.strategy.account.buffered_). + +.. _design.mps.strategy.account.buffered: strategy#account-buffered + +``typedef void (*SegBufferEmptyMethod)(Seg seg, Buffer buffer)`` + +_`.method.buffer-empty`: Free the unused part of the buffer to the +segment. Account the used part as new (see design.mps.strategy.account.new_) and the unused part as free (see design.mps.strategy.account.free_). + +.. _design.mps.strategy.account.new: strategy#account-new +.. _design.mps.strategy.account.free: strategy#account-free + + +Garbage collection +.................. + +``typedef Res (*SegAccessMethod)(Seg seg, Arena arena, Addr addr, AccessSet mode, MutatorContext context)`` + +_`.method.access`: The ``access`` method indicates that the client +program attempted to access the address ``addr``, but has been denied +due to a protection fault. The ``mode`` indicates whether the client +program was trying to read (``AccessREAD``) or write (``AccessWRITE``) +the address. If this can't be determined, ``mode`` is ``AccessREAD | +AccessWRITE``. The segment should perform any work necessary to remove +the protection whilst still preserving appropriate invariants (this +might scanning the region containing ``addr``). Segment classes are +not required to provide this method, and not doing so indicates they +never protect any memory managed by the pool. This method is called +via the generic function ``SegAccess()``. + +``typedef Res (*SegWhitenMethod)(Seg seg, Trace trace)`` + +_`.method.whiten`: The ``whiten`` method requests that the segment +``seg`` condemn (a subset of, but typically all) its objects for the +trace ``trace``. That is, prepare them for participation in the trace +to determine their liveness. The segment should expect fix requests +(`.method.fix`_) during the trace and a reclaim request +(`.method.reclaim`_) at the end of the trace. Segment +classes that automatically reclaim dead objects must provide this +method, and pools that use these segment classes must additionally set +the ``AttrGC`` attribute. This method is called via the generic +function ``SegWhiten()``. + +``typedef void (*SegGreyenMethod)(Seg seg, Trace trace)`` + +_`.method.grey`: The ``greyen`` method requires the segment ``seg`` to +colour its objects grey for the trace ``trace`` (excepting objects +that were already condemned for this trace). That is, make them ready +for scanning by the trace ``trace``. The segment must arrange that any +appropriate invariants are preserved, possibly by using the protection +interface (see design.mps.prot_). Segment 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. This +method is called via the generic function ``SegGreyen()``. + +.. _design.mps.prot: prot + +``typedef void (*SegBlackenMethod)(Seg seg, TraceSet traceSet)`` + +_`.method.blacken`: The ``blacken`` method is called if it is known +that the segment ``seg`` cannot refer to the white set for any of the +traces in ``traceSet``. The segment must blacken all its grey objects +for those traces. Segment 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. This method is +called via the generic function ``SegBlacken()``. + +``typedef Res (*SegScanMethod)(Bool *totalReturn, Seg seg, ScanState ss)`` + +_`.method.scan`: The ``scan`` method scans all the grey objects on the +segment ``seg``, passing the scan state ``ss`` to ``FormatScan``. The +segment may additionally accumulate a summary of *all* its objects. If +it succeeds in accumulating such a summary it must indicate that it +has done so by setting the ``*totalReturn`` parameter to ``TRUE``. +Otherwise it must set ``*totalReturn`` to ``FALSE``. Segment 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. This method is called via the generic function +``SegScan()``. + +``typedef Res (*SegFixMethod)(Seg seg, ScanState ss, Ref *refIO)`` + +_`.method.fix`: The ``fix`` method indicates that the reference +``*refIO`` has been discovered at rank ``ss->rank`` by the traces in +``ss->traces``, and the segment must handle this discovery according +to the fix protocol (design.mps.fix_). If the method moves the object, +it must update ``*refIO`` to refer to the new location of the object. +If the method determines that the referenced object died (for example, +because the highest-ranking references to the object were weak), it +must update ``*refIO`` to ``NULL``. Segment classes that automatically +reclaim dead objects must provide this method, and pools that use +these classes must additionally set the ``AttrGC`` attribute. Pool +classes that use segment classes that may move objects must also set +the ``AttrMOVINGGC`` attribute. The ``fix`` method is on the critical +path (see design.mps.critical-path_) and so must be fast. This method +is called via the function ``TraceFix()``. + +.. _design.mps.fix: fix +.. _design.mps.critical-path: critical-path + +_`.method.fixEmergency`: The ``fixEmergency`` method is used to +perform fixing in "emergency" situations. Its specification is +identical to the ``fix`` method, but it must complete its work without +allocating memory (perhaps by using some approximation, or by running +more slowly). Segment classes must provide this method if and only if +they provide the ``fix`` method. If the ``fix`` method does not need +to allocate memory, then it is acceptable for ``fix`` and +``fixEmergency`` to be the same. + +``typedef void (*SegReclaimMethod)(Seg seg, Trace trace)`` + +_`.method.reclaim`: The ``reclaim`` method indicates that any +remaining white objects in the segment ``seg`` have now been proved +unreachable by the trace ``trace``, and so are dead. The segment +should reclaim the resources associated with the dead objects. Segment +classes are not required to provide this method. If they do, pools +that use them must set the ``AttrGC`` attribute. This method is called +via the generic function ``SegReclaim()``. + +``typedef void (*SegWalkMethod)(Seg seg, Format format, FormattedObjectsVisitor f, void *v, size_t s)`` + +_`.method.walk`: The ``walk`` method must call the visitor function +``f`` (along with its closure parameters ``v`` and ``s`` and the +format ``format``) once for each of the *black* objects in the +segment ``seg``. Padding objects may or may not be included in the +walk, at the segment's discretion: it is the responsibility of the +client program to handle them. Forwarding objects must not be included +in the walk. Segment classes need not provide this method. This +method is called by the genetic function ``SegWalk()``, which is +called by the heap walker ``mps_arena_formatted_objects_walk()``. + +``typedef void (*SegFlipMethod)(Seg seg, Trace trace)`` + +_`.method.flip`: Raise the read barrier, if necessary, for a trace +that's about to flip and for which the segment is grey and potentially +contains references. + + Splitting and merging ..................... -``typedef Res (*SegSplitMethod)(Seg seg, Seg segHi, Addr base, Addr mid, Addr limit, Bool withReservoirPermit)`` +``typedef Res (*SegSplitMethod)(Seg seg, Seg segHi, Addr base, Addr mid, Addr limit)`` _`.method.split`: Segment subclasses may extend the support for segment splitting by defining their own "split" method. On entry, @@ -242,17 +393,20 @@ split. The method is responsible for destructively modifying ``seg`` and initializing ``segHi`` so that on exit ``seg`` is a segment with region ``[base,mid)`` and ``segHi`` is a segment with region ``[mid,limit)``. Usually a method would only directly modify the -fields defined for the segment subclass. This might involve -allocation, which may use the reservoir if ``withReservoirPermit`` is -``TRUE``. +fields defined for the segment subclass. _`.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: 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)`` +_`.method.split.accounting`: If ``seg`` belongs to a generation in a +chain, then the pool generation accounting must be updated. In the +simple case where the split segments remain in the same generation, +this can be done by calling ``PoolGenAccountForSegSplit()``. + +``typedef Res (*SegMergeMethod)(Seg seg, Seg segHi, Addr base, Addr mid, Addr limit)`` _`.method.merge`: Segment subclasses may extend the support for segment merging by defining their own ``merge`` method. On entry, @@ -261,24 +415,29 @@ segment with region ``[mid,limit)``, The method is responsible for destructively modifying ``seg`` and finishing ``segHi`` so that on exit ``seg`` is a segment with region ``[base,limit)`` and ``segHi`` is garbage. Usually a method would only modify the fields defined for -the segment subclass. This might involve allocation, which may use the -reservoir if ``withReservoirPermit`` is ``TRUE``. +the segment subclass. _`.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: protocol#overview.next-method +.. _design.mps.protocol.overview.next-method: protocol#overview-next-method + +_`.method.merge.accounting`: If ``seg`` belongs to a generation in a +chain, then the pool generation accounting must be updated. In the +simple case where the two segments started in the same generation and +the merged segment remains in that generation, this can be done by +calling ``PoolGenAccountForSegMerge()``. _`.split-merge.shield`: Split and merge methods may assume that the -segments they are manipulating are not in the shield cache. +segments they are manipulating are not in the shield queue. -_`.split-merge.shield.flush`: The shield cache is flushed before any +_`.split-merge.shield.flush`: The shield queue is flushed before any split or merge methods are invoked. _`.split-merge.shield.re-flush`: If a split or merge method performs -an operation on a segment which might cause the segment to be cached, -the method must flush the shield cache before returning or calling +an operation on a segment which might cause the segment to be queued, +the method must flush the shield queue before returning or calling another split or merge method. _`.split-merge.fail`: Split and merge methods might fail, in which @@ -292,7 +451,7 @@ before calling the next method, the appropriate anti-method must be 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 +.. _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 diff --git a/mps/design/shield.txt b/mps/design/shield.txt index 31648245ed1..9a82c7eead3 100644 --- a/mps/design/shield.txt +++ b/mps/design/shield.txt @@ -9,7 +9,7 @@ Shield :Status: incomplete guide :Revision: $Id$ :Copyright: See `Copyright and License`_. -:Index terms: pair: shield; design +:Index terms: pair: shield; design Introduction @@ -25,130 +25,245 @@ _`.readership`: Any MPS developer. Not confidential. Overview -------- -_`.over`: For incremental collection, we need *separate control* of -collector access and mutator (client) access to memory. The collector +_`.overview`: The MPS implements incremental garbage collection using +memory barriers implemented by a combination of hardware memory +protection and thread control. The MPS needs *separate control* of +collector access and mutator (client) access to memory: the collector must be able to incrementally scan objects, without the mutator being able to see them yet. -Unfortunately common OSs do not support different access levels -(protection maps) for different parts of the same process. +Unfortunately common operating systems do not support different access +levels (protection maps) for different parts of the same process. The MPS Shield is an abstraction that does extra work to overcome this limitation, and give the rest of the MPS the illusion that we can control collector and mutator access separately. -Control of mutator access -------------------------- +Interface +--------- -The MPS uses ``ShieldRaise()`` and ``ShieldLower()`` to forbid or -permit the mutator access to object memory (that is, memory allocated -by MPS). + +Mutator access +.............. + +The shield provides ``ShieldRaise()`` and ``ShieldLower()`` to forbid +or permit the mutator access to object memory segments. Between these +two, a segment is said to have the shield *raised* (`.def.raised`_). ``void ShieldRaise(Arena arena, Seg seg, AccessSet mode)`` - Prevent the mutator accessing the memory in the specified mode - (``AccessREAD``, ``AccessWRITE``, or both). + Prevent the mutator accessing the memory segment in the specified + mode (``AccessREAD``, ``AccessWRITE``, or both). ``void ShieldLower(Arena arena, Seg seg, AccessSet mode)`` - Allow the mutator to access the memory in the specified mode - (``AccessREAD``, ``AccessWRITE``, or both). + Allow the mutator to access the memory segment in the specified + mode (``AccessREAD``, ``AccessWRITE``, or both). -If the mutator attempts an access that hits a shield, the MPS gets a -barrier hit (in the form of a fault, interrupt, exception), quickly -does some necessary work, and then makes the access succeed. +If the mutator attempts an access that hits the shield, the MPS gets +an OS-specific hardware protection fault which reaches +``ArenaAccess()``, does whatever work is necessary, then lowers the +shield and returns to the mutator. -Some objects (for example registers) cannot be hardware protected: the -only way to prevent mutator access to them is to halt all mutator -threads. The MPS uses ``ShieldSuspend()`` and ``ShieldResume()`` to do -this. - -``void ShieldSuspend(Arena arena)`` - - Stop all registered mutator threads. - -``void ShieldResume(Arena arena)`` - - Resume all registered mutator threads. +``ShieldRaise()`` and ``ShieldLower()`` do *not* nest. -Control of collector access ---------------------------- +Entering the shield +................... -When the collector wants to access object memory (that is, memory -allocated by MPS), it must first call ``ShieldEnter()``, then wrap any -accesses with a ``ShieldExpose()`` and ``ShieldCover()`` pair, and -finally call ``ShieldLeave()``. +The MPS can only gain exclusive access from *inside* the shield +(`.def.inside`_). To enter the shield, the MPS must call +``ShieldEnter()``, and to leave it, the MPS must call +``ShieldLeave()``. ``ShieldEnter()`` and ``ShieldLeave()`` are called by ``ArenaEnter()`` -and ``ArenaLeave()`` (approximately) -- so the shield is always -entered when we are within MPS code (approximately). +and ``ArenaLeave()`` so almost all of the MPS is is inside the +shield. -``ShieldExpose()`` might for example be called around: -- format-scan (when scanning); -- format-skip (when marking grains in a non-moving fix); -- format-isMoved and ``AddrCopy()`` (during a copying fix); -- format-pad (during reclaim). +Collector access to segments +............................ + +When the MPS wants to access object memory segments from inside the +shield, it must wrap any accesses with a ``ShieldExpose()`` and +``ShieldCover()`` pair. These calls nest. After a call to +``ShieldExpose()`` a segment is said to be *exposed* until the last +nested call to ``ShieldCover()``. The shield arranges that the MPS can +access the memory while it is exposed. + +A segment might for example be exposed during: + + - format-scan (when scanning); + - format-skip (when marking grains in a non-moving fix); + - format-isMoved and ``AddrCopy()`` (during a copying fix); + - format-pad (during reclaim). Note that there is no need to call ``ShieldExpose()`` when accessing -pool management memory such as bit tables. This is not object -memory, is never (legally) accessed by the mutator, and so is never -shielded. +pool management memory such as bit tables. This is not object memory, +is never (legally) accessed by the mutator, and so is never shielded. -On common operating systems, the only way to allow collector access is -to allow access from the whole process, including the mutator. So if -the Shield is asked to allow collector access but deny mutator access, -it will halt all mutator threads to prevent any mutator access. The -Shield performs suspension and restart; normal collector code does not -need to worry about it. +Similarly, a pool class that never raises the shield on its segments +need never expose them to gain access. -Collector code can make multiple sequential, overlapping, or nested -calls to ``ShieldExpose()`` on the same segment, as long as each is -balanced by a corresponding ``ShieldCover()`` before ``ShieldLeave()`` -is called). A usage count is maintained on each segment in -``seg->depth``: a positive "depth" means a positive number of -outstanding *reasons* why the segment must be exposed to the collector. -When the usage count reaches zero, there is no longer any reason the -segment should be unprotected, and the Shield could re-instate -hardware protection. -However, as a performance-improving hysteresis, the Shield defers -re-protection, maintaining a cache of the last ``ShieldCacheSIZE`` -times a segment no longer had a reason to be collector-accessible. -Presence in the cache counts as a reason: segments in the cache have -``seg->depth`` increased by one. As segments get pushed out of the -cache, or at ``ShieldLeave()``, this artificial reason is -decremented from ``seg->depth``, and (if ``seg->depth`` is now zero) -the deferred reinstatement of hardware protection happens. +Collector access to the unprotectable +..................................... -So whenever hardware protection is temporarily removed to allow -collector access, there is a *nurse* that will ensure this protection -is re-established: the nurse is either the balancing ``ShieldCover()`` -call in collector code, or an entry in the shield cache. +When the MPS wants to access an unprotectable object from inside the +shield, it must wrap any accesses with a ``ShieldHold()`` and +``ShieldRelease()`` pair. This allows access to objects which cannot +be shielded by ``ShieldRaise()``, such as: -.. note:: + - the stack and registers of mutator threads, + - lockless allocation point structures, + - areas of memory that can't be protected by operating system calls, + - unprotectable roots. - 1. Why is there a fixed-size cache? This is not the simple - approach! All we need is a chain of segs that might need their - hardware protection to be sync'd with their shield mode. Head - in the shield, and one pointer in each seg struct. I guess we - try hard to avoid bloating ``SegStruct`` (to maintain residency - in the processor cache). But is 16 the right size? A cache-miss - wastes two kernel calls. +``void ShieldHold(Arena arena)`` - 2. I don't like the cache code. For example, why does - ``ShieldFlush()`` break out early if ``arena->shDepth`` is 0? - This should never happen until the cache is completely flushed, - that is, we have reached ``shCacheLimit``. Why does - ``ShieldFlush()`` not reset ``shCacheLimit``? Why does - ``flush()`` silently accept ``NULL`` cache entries? + Get exclusive access to the unprotectable. - 3. Why is ``seg->depth`` never checked for overflow? It is only a - 4-bit-wide bit field, currently. +``void ShieldRelease(Arena arena)`` - Richard Kistruck, 2006-12-19. + Declare that exclusive access is no longer needed. + + +Mechanism +--------- + +On common operating systems, the only way to allow the MPS access is +to allow access from the whole process, including the mutator. So +``ShieldExpose()`` will suspend all mutator threads to prevent any +mutator access, and so will ``ShieldRaise()`` on an unexposed segment. +The shield handles suspending and resuming threads, and so the rest of +the MPS does not need to worry about it. + +The MPS can make multiple sequential, overlapping, or nested calls to +``ShieldExpose()`` on the same segment, as long as each is balanced by +a corresponding ``ShieldCover()`` before ``ShieldLeave()`` is called. +A usage count is maintained on each segment in ``seg->depth``. When +the usage count reaches zero, there is no longer any reason the +segment should be unprotected, and the shield may reinstate hardware +protection at any time. + +However, as a performance-improving hysteresis, the shield defers +re-protection, maintaining a queue of segments that require attention +before mutator threads are resumed (`.impl.delay`_). While a segment +is in the queue, it has ``seg->queued`` set true. + +This hysteresis allows the MPS to proceed with garbage collection +during a pause without actually setting hardware protection until it +returns to the mutator. This is particularly important on operating +systems where the protection is expensive and poorly implemented, such +as macOS. + +The queue also ensures that no memory protection system calls will be +needed for incremental garbage collection if a complete collection +cycle occurs during one pause. + + +Implementation +-------------- + +_`.impl.delay`: The implementation of the shield avoids suspending +threads for as long as possible. When threads are suspended, it +maintains a queue of segments where the desired and actual protection +do not match. This queue is flushed on leaving the shield. + + +Definitions +........... + +_`.def.raised`: A segment has the shield *raised* for an access mode +after a call to ``ShieldRaise()`` and before a call to +``ShieldLower()`` with that mode. + +_`.def.exposed`: A segment is *exposed* after a call to +``ShieldExpose()`` and before a call to ``ShieldLower()``. + +_`.def.synced`: A segment is *synced* if the prot and shield modes are +the same, and unsynced otherwise. + +_`.def.depth`: The *depth* of a segment is defined as: + + | depth ≔ #exposes − #covers, where + | #exposes = the number of calls to ``ShieldExpose()`` on the segment + | #covers = the number of calls to ``ShieldCover()`` on the segment + +``ShieldCover()`` must not be called without a matching +``ShieldExpose()``, so this figure must always be non-negative. + +_`.def.total.depth`: The total depth is the sum of the depth over all +segments. + +_`.def.outside`: Being outside the shield is being between calls to +``ShieldLeave()`` and ``ShieldEnter()``, and similarly _`.def.inside`: +being inside the shield is being between calls to ``ShieldEnter()`` +and ``ShieldLeave()``. [In a multi-threaded MPS this would be +per-thread. RB 2016-03-18] + +_`.def.shielded`: A segment is shielded if the shield mode is +non-zero. [As set by ShieldRaise.] + + +Properties +.......... + +_`.prop.outside.running`: The mutator may not be suspended while outside +the shield. + +_`.prop.mutator.access`: An attempt by the mutator to access shielded +memory must be pre-empted by a call to ``ArenaAccess()``. + +_`.prop.inside.access`: Inside the shield the MPS must be able to access +all unshielded segments and all exposed segments. + + +Invariants +.......... + +_`.inv.outside.running`: The mutator is not suspended while outside the +shield. + +_`.inv.unsynced.suspended`: If any segment is not synced, the mutator is +suspended. + +_`.inv.unsynced.depth`: All unsynced segments have positive depth or are +in the queue. + +_`.inv.outside.depth`: The total depth is zero while outside the shield. + +_`.inv.prot.shield`: The prot mode is never more than the shield mode. + +_`.inv.expose.depth`: An exposed segment's depth is greater than zero. + +_`.inv.expose.prot`: An exposed segment is not protected in the mode +it was exposed with. + + +Proof Hints +........... + +Hints at proofs of properties from invariants. + +_`.proof.outside`: `.inv.outside.running`_ directly ensures +`.prop.outside.running`_. + +_`.proof.sync`: As the depth of a segment cannot be negative + + | total depth = 0 + | ⇒ for all segments, depth = 0 + | ⇒ all segments are synced (by `.inv.unsynced.depth`_) + +_`.proof.access`: If the mutator is running then all segments must be +synced (`.inv.unsynced.suspend`_). Which means that the hardware +protection (protection mode) must reflect the software protection +(shield mode). Hence all shielded memory will be hardware protected +while the mutator is running. This ensures `.prop.mutator.access`_. + +_`.proof.inside`: `.inv.prot.shield`_ and `.inv.expose.prot`_ ensure +`.prop.inside.access`_. Initial ideas @@ -158,6 +273,100 @@ _`.ideas`: There never was an initial design document, but [RB_1995-11-29]_ and [RB_1995-11-30]_ contain some initial ideas. +Improvement Ideas +----------------- + + +Mass exposure +............. + +_`.improv.mass-expose`: If protection calls have a high overhead it might +be good to pre-emptively unprotect large ranges of memory when we +expose one segment. With the current design this would mean +discovering adjacent shielded segments and adding them to the queue. +The collector should take advantage of this by preferentially scanning +exposed segments during a pause. + + +Segment independence +.................... + +_`.improv.noseg`: The shield is implemented in terms of segments, using +fields in the segment structure to represent its state. This forces us +to (for example) flush the shield queue when deleting a segment. The +shield could keep track of protection and shielding independently, +possibly allowing greater coalescing and more efficient and flexible +use of system calls (see `.improv.mass-expose`_). + + +Concurrent collection +..................... + +_`.improv.concurrent`: The MPS currently does not collect +concurrently, however the only thing that makes it not-concurrent is a +critical point in the Shield abstraction where the MPS seeks to gain +privileged access to memory (usually in order to scan it). The +critical point is where ``ShieldExpose()`` in shield.c has to call +``ShieldHold()`` to preserve the shield invariants. This is the only +point in the MPS that prevents concurrency, and the rest of the MPS is +designed to support it. + +The restriction could be removed if either: + + * the MPS could use a different set of protections to the mutator + program + + * the mutator program uses a software barrier + +The first one is tricky, and the second one just hasn't come up in any +implementation we've been asked to make yet. Given a VM, it could +happen, and the MPS would be concurrent. + +So, I believe there's nothing fundamentally non-concurrent about the +MPS design. It's kind of waiting to happen. + +(Originally written at .) + + +Early Resume +............ + +_`.improv.resume`: There is a tradeoff between delaying flushing the +shield queue (preventing unnecessary protection and allowing us to +coalesce) and resuming mutator threads. We could resume threads +earlier under some circumstances, such as before reclaim (which does +not need to interact with the mutator). Basically, it might be worth +resuming the mutator early in a pause if we know that we're unlikely +to suspend it again (no more calls to ``ShieldRaise()`` or +``ShieldExpose()`` on shielded segments). + + +Expose modes +............ + +_`.improv.expose-modes`: Would it be a good idea for +``ShieldExpose()`` to take an ``AccessSet``? It might be good if we +didn't have to raise a write barrier unless we want to write. When +scanning (for instance), we may not need to write, so when scanning a +segment behind a write barrier we shouldn't have to call +``mprotect()``. That's a bit speculative: how often do we scan a +segment and not write to it. Alternatively, and more speculatively, we +could keep the write barrier up, handle the (possibly nested) trap and +*then* expose the shield. I'm just scraping around for ways to reduce +calls to ``mprotect()``. + +Theoretically we can do this, but: + + 1. We're mostly a moving collector so we'll almost always want to + write to segments we scan. That could change if we do more + non-moving collection. + + 2. The main cost of protection is changing it at all, not whether we + change just read or write. On macOS, the main cost seems to be the + TLB flush, which affects wall-clock time of everything on the + processor! + + References ---------- @@ -183,14 +392,22 @@ Document History - 2013-05-24 GDR_ Converted to reStructuredText. +- 2016-03-17 RB_ Updated for dynamic queueing and general code tidying + that has removed complaints. + +- 2016-03-19 RB_ Updated for separate queued flag on segments, changes + of invariants, cross-references, and ideas for future improvement. + .. _GDR: http://www.ravenbrook.com/consultants/gdr/ +.. _RB: http://www.ravenbrook.com/consultants/rb/ + Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2013-2018 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 diff --git a/mps/design/sig.txt b/mps/design/sig.txt index 23f55a881a1..98ab1f489e0 100644 --- a/mps/design/sig.txt +++ b/mps/design/sig.txt @@ -177,8 +177,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2016 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2013-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 diff --git a/mps/design/sp.txt b/mps/design/sp.txt index 47dab436b4e..5d9fd72e17f 100644 --- a/mps/design/sp.txt +++ b/mps/design/sp.txt @@ -38,8 +38,8 @@ _`.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.) +would cause a stack overflow fault, causing the mutator to enter the +MPS recursively, which would fail because the lock is held.) Design @@ -88,22 +88,24 @@ documented in the manual. ==== ====== ======================== Args Locals Function ==== ====== ======================== - 5 0 ``PoolAccess()`` - 5 0 ``PoolSegAccess()`` - 3 5 ``TraceSegAccess()`` + 5 0 ``SegAccess()`` + 5 0 ``SegWholeAccess()`` + 3 8 ``TraceSegAccess()`` 4 1 ``traceScanSeg()`` - 4 8 ``traceScanSegRes()`` - 4 0 ``PoolScan()`` - 4 5 ``AMCScan()`` + 4 9 ``traceScanSegRes()`` + 4 0 ``SegScan()`` + 4 5 ``amcSegScan()`` + 4 0 ``FormatScan()`` 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()`` + 3 0 ``SegFix()`` + 4 15 ``amcSegFix()`` + 3 5 ``BufferFill()`` + 5 11 ``AMCBufferFill()`` + 5 73 ``PoolGenAlloc()`` + 6 5 ``SegAlloc()`` + 4 4 ``ArenaAlloc()`` + 5 6 ``PolicyAlloc()`` + 6 10 ``ArenaFreeLandAlloc()`` 7 1 ``LandFindInZones()`` 7 16 ``cbsFindInZones()`` 5 3 ``cbsFindFirst()`` @@ -111,13 +113,12 @@ Args Locals Function 3 7 ``SplaySplay()`` 4 8 ``SplaySplitDown()`` 3 0 ``SplayZig()`` - 109 ≤190 **Total** + 112 ≤258 **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. +We expect that a compiler will not need to push all local variables +onto the stack, but even in the case where it pushes all of them, this +call requires no more than 370 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 @@ -186,8 +187,8 @@ Document History Copyright and License --------------------- -Copyright © 2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2014-2018 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 diff --git a/mps/design/splay.txt b/mps/design/splay.txt index 0e77835f734..c1dfa8bb977 100644 --- a/mps/design/splay.txt +++ b/mps/design/splay.txt @@ -219,16 +219,16 @@ the root of the splay tree. It is intended that the `.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)`` +``typedef Bool (*SplayTestNodeFunction)(SplayTree splay, Tree tree, void *closure)`` _`.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 +`.usage.test.node`_ for an example). The ``closure`` +parameter describes the environment for the function (see `.function.splay.find.first`_ and `.function.splay.find.last`_). -``typedef Bool (*SplayTestTreeFunction)(SplayTree splay, Tree tree, void *closureP, Size closureS)`` +``typedef Bool (*SplayTestTreeFunction)(SplayTree splay, Tree tree, void *closure)`` _`.type.splay.test.tree.function`: A function of type ``SplayTestTreeFunction`` is required to determine whether any of the @@ -237,7 +237,7 @@ 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`` function (see `.type.splay.test.node.function`_) would -return ``TRUE``. Parameters ``closureP`` and ``closureS`` describe the +return ``TRUE``. The ``closure`` parameter describes the environment for the function (see `.function.splay.find.first`_ and `.function.splay.find.last`_). @@ -348,18 +348,17 @@ 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)`` +``Bool SplayFindFirst(Tree *nodeReturn, SplayTree splay, SplayTestNodeFunction testNode, SplayTestTreeFunction testTree, void *closure)`` _`.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``; +``testTree`` functions (see `.req.property.find`_). ``closure`` +is an arbitrary value, and is passed to the ``testNode`` +and ``testTree`` functions. 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)`` +``Bool SplayFindLast(Tree *nodeReturn, SplayTree splay, SplayTestNodeFunction testNode, SplayTestTreeFunction testTree, void *closure)`` _`.function.splay.find.last`: As ``SplayFindFirst()``, but find the last node in the tree that satisfies the client property. @@ -520,13 +519,13 @@ _`.usage.compare`: Comparison function (see `.type.tree.compare.function`_):: _`.usage.test.tree`: Test tree function (see `.type.splay.test.tree.function`_):: - Bool FreeBlockTestTree(SplayTree splay, Tree tree - void *closureP, Size closureS) { - /* Closure environment has wanted size as value of closureS. */ + Bool FreeBlockTestTree(SplayTree splay, Tree tree, + void *closure) { + /* Closure environment has wanted size as value of *closure. */ /* Look at the cached value for the node to see if any */ /* blocks in the subtree are big enough. */ - Size size = closureS; + Size size = *(Size *)closure; FreeBlock freeNode = FreeBlockOfTree(tree); return freeNode->maxSize >= size; } @@ -534,12 +533,12 @@ _`.usage.test.tree`: Test tree function (see _`.usage.test.node`: Test node function (see `.type.splay.test.node.function`_):: - Bool FreeBlockTestNode(SplayTree splay, Tree tree - void *closureP, Size closureS) { - /* Closure environment has wanted size as value of closureS. */ + Bool FreeBlockTestNode(SplayTree splay, Tree tree, + void *closure) { + /* Closure environment has wanted size as value of *closure. */ /* Look at the size of the node to see if is big enough. */ - Size size = closureS; + Size size = *(Size *)closure; FreeBlock freeNode = FreeBlockOfTree(tree); return freeNode->size >= size; } @@ -610,7 +609,7 @@ block:: Bool found; /* look for the first node of at least the given size. */ - /* closureP parameter is not used. See `.function.splay.find.first.`_ */ + /* closure parameter is not used. See `.function.splay.find.first.`_ */ found = SplayFindFirst(&splayNode, splayTree, FreeBlockTestNode, FreeBlockTestTree, NULL, size); @@ -956,8 +955,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +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 diff --git a/mps/design/ss.txt b/mps/design/ss.txt deleted file mode 100644 index 8ecd6a18153..00000000000 --- a/mps/design/ss.txt +++ /dev/null @@ -1,172 +0,0 @@ -.. 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 hot end of the stack (``stackHot``), -and spilled the root registers into the next ``n`` words, -implementations call the generic higher-order function -``StackScanInner(ss, stackCold, stackHot, n, scan_area, closure)`` to actually do the scanning. - - -Interface ---------- - -``Res StackScan(ScanState ss, Word *stackCold, - mps_area_scan_t scan_area, - void *closure)`` - -_`.if.scan`: Scan the root registers of the current thread, and the -control stack between ``stackCold`` and the hot end of the stack, in -the context of the given scan state, using ``scan_area``. 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/mps/design/sso1al.txt b/mps/design/sso1al.txt deleted file mode 100644 index bcf2905f198..00000000000 --- a/mps/design/sso1al.txt +++ /dev/null @@ -1,193 +0,0 @@ -.. mode: -*- rst -*- - -Stack scanner for Digital Unix on Alpha -======================================= - -:Tag: design.mps.sso1al -:Author: David Jones -:Date: 1997-03-27 -:Status: draft document -:Revision: $Id$ -:Copyright: See `Copyright and License`_. -:Index terms: - pair: Digital Unix on Alpha stack scanner; design - pair: Digital Unix on Alpha; stack scanner design - -.. warning:: - - As of 2013-05-26, the MPS is no longer supported on Digital Unix, - so this document is only of historical interest. - - -Introduction ------------- - -_`.readership`: Any MPS developer. - -_`.intro`: This is the design for Stack Scanner module that runs on -Digital UNIX / Alpha systems (See os.o1 and arch.al). The design -adheres to the general design and interface described (probably not -described actually) in design.mps.ss. - -_`.source.alpha`: book.digital96 (Alpha Architecture Handbook) -describes the Alpha Architecture independently of any particular -implementation. The instruction mnemonics and the semantics for each -instruction are specified in that document. - -[DEC_Assembler]_ describes the assembler syntax and assembler -directives. It also summarises the calling conventions used. Chapters -1 and 6 were especially useful, especially chapter 6. - -[DEC_Alpha_Calling_Standard]_ describes the calling conventions used -for Digital Alpha systems. Chapter 2 was useful. But the whole -document was not used as much as the previous 2 documents. - - -Definitions ------------ - -_`.def.saved`: Saved Register. A saved register is one whose value is defined to -be preserved across a procedure call according to the Calling Standard. They -are ``$9``--``$15``, ``$26``, and ``$30``. ``$30`` is the stack pointer. - -_`.def.non-saved`: Non-Saved Register. A non-save register is a -register that is assumed to be modified across a procedure call -according to the Calling Standard. - -_`.def.tos`: Top of Stack. The top of stack is the youngest portion of -the stack. - -_`.def.bos`: Bottom of Stack. The bottom of stack is the oldest -portion of the stack. - -_`.def.base`: Base. Of a range of addresses, the base is the lowest -address in the range. - -_`.def.limit`: Limit. Of a range of addresses, the limit is "one past" -the highest address in the range. - - -Overview --------- - -_`.overview`: The registers and the stack need to be scanned. This is -achieved by storing the contents of the registers into a frame at the -top of the stack and then passing the base and limit of the stack -region, including the newly created frame, to the function -``TraceScanAreaTagged()``. ``TraceScanAreaTagged()`` performs the -actual scanning and fixing. - - -Detail Design -------------- - -Functions -......... - -_`.fun.stackscan`: ``StackScan()`` - -_`.fun.stackscan.asm`: The function is written in assembler. -_`.fun.stackscan.asm.justify`: This is because the machine registers -need to be examined, and it is only possible to access the machine -registers using assembler. - -_`.fun.stackscan.entry`: On entry to this procedure all the non-saved -(temporary) registers that contain live pointers must have been saved -in some root (usually the stack) by the mutator (otherwise it would -lose the values). Therefore only the saved registers need to be stored -by this procedure. - -_`.fun.stackscan.assume.saved`: We assume that all the saved registers -are roots. This is conservative since some of the saved registers -might not be used. - -_`.fun.stackscan.frame`: A frame is be created on the top of the -stack. _`.fun.stackscan.frame.justify`: This frame is used to store -the saved registers into so that they can be scanned. - -_`.fun.stackscan.save`: All the saved registers, apart from $30 the -stack pointer, are to be stored in the frame. -_`.fun.stackscan.save.justify`: This is so that they can be scanned. -The stack pointer itself is not scanned as the stack is assumed to be -a root (and therefore a priori alive). - -_`.fun.stackscan.call`: ``TraceScanAreaTagged()`` is called with the -current stack pointer as the base and the (passed in) ``StackBot`` as -the limit of the region to be scanned. _`.fun.stackscan.call.justify`: -This function does the actual scanning. The Stack on Alpha systems -grows down so the stack pointer (which points to the top of the stack) -is lower in memory than the bottom of the stack. - -_`.fun.stackscan.return`: The return value from -``TraceScanAreaTagged()`` is used as the return value for -``StackScan()``. - - -References ----------- - -.. [DEC_Assembler] - "Assembly Language Programmer's Guide"; - Digital Equipment Corporation; 1996; - . - -.. [DEC_Alpha_Calling_Standard] - "Calling Standard for Alpha Systems"; - Digital Equipment Corporation; 1996; - . - - -Document History ----------------- - -- 1997-03-27 David Jones. Draft 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/mps/design/stack-scan-areas.svg b/mps/design/stack-scan-areas.svg new file mode 100644 index 00000000000..285cdad802b --- /dev/null +++ b/mps/design/stack-scan-areas.svg @@ -0,0 +1,553 @@ + + + + + + + 2016-03-08 09:44Z + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + mutator stack + + + + mpsi frame + + + + reference + + + + + heap + object + + + + + callee-save register + + + + jmp_buf + + + + + internal MPS stack + + + + cold + + + warm + + + hot + + + + + StackScan frame + + + + + + + heap + object + + + + + + heap + object + + + + + + + scanned + area + + + + diff --git a/mps/design/stack-scan.txt b/mps/design/stack-scan.txt new file mode 100644 index 00000000000..05e6dbccbd0 --- /dev/null +++ b/mps/design/stack-scan.txt @@ -0,0 +1,386 @@ +.. mode: -*- rst -*- + +Stack and register scanning +=========================== + +:Tag: design.mps.stack-scan +: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 (the one that has called +in to the MPS). + +_`.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 + +_`.origin`: This design was originally proposed in +mail.richard.2012-08-03.14-36_. + +.. _mail.richard.2012-08-03.14-36: https://info.ravenbrook.com/mail/2012/08/03/14-36-35/0/ + + +Requirements +------------ + +_`.req.stack.hot`: Must locate the hot end of the mutator's stack. (This +is needed for conservative garbage collection of uncooperative code, +where references might be stored by the mutator on its stack.) + +_`.req.stack.cold.not`: There is no requirement to locate the cold end +of the stack. (The mutator supplies this as an argument to +``mps_root_create_thread()``.) + +_`.req.stack.platform`: Must support the platform's stack +conventions. + +_`.req.stack.platform.full-empty`: The implementation must take into +account whether the stack is *full* (the stack pointer points to the +last full location) or *empty* (the stack pointer points to the +first empty location). + +_`.req.stack.platform.desc-asc`: The implementation must take into +account whether the stack is *descending* (the hot end of the stack is +at a lower address than the cold end) or *ascending* (the hot end of +the stack is at a higher address than the cold end). + +_`.req.registers`: Must locate and scan all references in the +mutator's *root registers*, the subset of registers which might +contain references that do not also appear on the stack. (This is +needed for conservative garbage collection of uncooperative code, +where references might appear in registers.) + +_`.req.entry`: Should save the mutator's context (stack and registers) +at the point where it enters the MPS. (This avoids scanning registers +and stack that belong to the MPS rather than the mutator, leading to +unnecessary pinning and zone pollution; see job003525_.) + +.. _job003525: http://www.ravenbrook.com/project/mps/issue/job003525/ + +_`.req.setjmp`: The implementation must follow the C Standard in its +use of the ``setjmp()`` macro. (So that it is reliable and portable.) + +_`.req.assembly.not`: The implementation should not use assembly +language. (So that it can be developed in tools like Microsoft Visual +Studio that don't support this.) + + +Design +------ + +_`.sol.entry-points`: To meet `.req.entry`_, the mutator's registers +and stack must be recorded when the mutator enters the MPS, if there +is a possibility that the MPS might need to know the mutator context. + +_`.sol.entry-points.fragile`: The analysis of which entry points might +need to save the context (see `.anal.entry-points`_ below) is fragile. +It might be incorrect now, or become incomplete if we refactor the +internals of tracing and polling. As a defence against errors of this +form, ``StackScan()`` asserts that the context was saved, but if the +client program continues from the assertion, it saves the context +anyway and continues. + +_`.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. + +_`.sol.setjmp`: The values in callee-save registers can be found by +invoking ``setjmp()``. This forces any of the caller's callee-save +registers into either the ``jmp_buf`` or the current stack frame. + +_`.sol.setjmp.scan`: Although we might be able to decode the jump +buffer in a platform-dependent way, it's hard to guarantee that an +uncooperative compiler won't temporarily store a reference in any +register or stack location. We must conservatively scan the whole of +both. + +_`.sol.setjmp.justify`: The [C1990]_ 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. + +We believe that any reasonable implementation of ``setjmp()`` must +copy the callee-save registers either into the jump buffer or into the +stack frame that invokes it in order to work as described. Otherwise, +once the callee-save registers have been overwritten by other function +calls, a ``longjmp()`` would result in the callee-save registers +having the wrong values. A ``longjmp()`` can come from anywhere, and +so the function using ``setjmp()`` can't rely on callee-save registers +being saved by callees. + +_`.sol.stack.hot`: We could decode the frame of the function that +invokes ``setjmp()`` from the jump buffer in a platform-specific way, +but we can do something simpler (if more hacky) by calling the stub +function ``StackHot()`` which takes the address of its argument. So +long as this stub function is not inlined into the caller, then on all +supported platforms this yields a pointer that is pretty much at the +hot end of the frame. + +_`.sol.stack.hot.noinline`: The reason that ``StackHot()`` must not be +inlined is that after inlining, the compiler might place ``stackOut`` +at a colder stack address than the ``StackContextStruct``, causing the +latter not to be scanned. See `mail.gdr.2018-07-11.09-48`_. + +.. _mail.gdr.2018-07-11.09-48: https://info.ravenbrook.com/mail/2018/07/11/09-48-49/0/ + +_`.sol.stack.nest`: We can take care of scanning the jump buffer +itself by storing it in the same stack frame. That way a scan from the +hot end determined by `.sol.stack.hot`_ to the cold end will contain +all of the roots. + +_`.sol.stack.platform`: As of version 1.115, all supported platforms +are *full* and *descending* so the implementation in ``StackScan()`` +assumes this. New platforms must check this assumption. + +_`.sol.xc.alternative`: On macOS, we could use ``getcontext()`` from +libunwind (see here_), but that produces deprecation warnings and +introduces a dependency on that library. + +.. _here: http://stackoverflow.com/questions/3592914/how-can-i-implement-cooperative-lightweight-threading-with-c-on-mac-os-x + + +Analysis +-------- + +_`.anal.setjmp`: The [C1990]_ standard says: + + An invocation of the ``setjmp`` macro shall appear only in one of + the following contexts: + + - the entire controlling expression of a selection or iteration + statement; + + - one operand of a relational or equality operator with the other + operand an integral constant expression, with the resulting + expression being the entire controlling expression of a + selection or iteration statement; + + - the operand of a unary ``!`` operator with the resulting + expression being the entire controlling expression of a + selection or iteration statement; or + + - the entire expression of an expression statement (possibly cast + to ``void``). + +And the [C1999]_ standard adds: + + If the invocation appears in any other context, the behavior is + undefined. + +_`.anal.entry-points`: Here's a reverse call graph (in the master +sources at changelevel 189652) showing which entry points might call +``StackScan()`` and so need to record the stack context:: + + StackScan + └ThreadScan + └RootScan + ├traceScanRootRes + │ └traceScanRoot + │ └rootFlip + │ └traceFlip + │ └TraceStart + │ ├PolicyStartTrace + │ │ └TracePoll + │ │ ├ArenaStep + │ │ │ └mps_arena_step + │ │ └ArenaPoll + │ │ ├mps_alloc + │ │ ├mps_ap_fill + │ │ ├mps_ap_fill_with_reservoir_permit + │ │ ├mps_ap_alloc_pattern_end + │ │ ├mps_ap_alloc_pattern_reset + │ │ └ArenaRelease + │ │ ├mps_arena_release + │ │ └ArenaStartCollect + │ │ ├mps_arena_start_collect + │ │ └ArenaCollect + │ │ └mps_arena_collect + │ └TraceStartCollectAll + │ ├ArenaStep [see above] + │ ├ArenaStartCollect [see above] + │ └PolicyStartTrace [see above] + └rootsWalk + └ArenaRootsWalk + └mps_arena_roots_walk + +So the entry points that need to save the stack context are +``mps_arena_step()``, ``mps_alloc()``, ``mps_ap_fill()``, +``mps_ap_fill_with_reservoir_permit()``, +``mps_ap_alloc_pattern_end()``, ``mps_ap_alloc_pattern_reset()``, +``mps_arena_release()``, ``mps_arena_start_collect()``, +``mps_arena_collect()``, and ``mps_arena_roots_walk()``. + + +Interface +--------- + +``typedef StackContextStruct *StackContext`` + +_`.if.sc`: A structure encapsulating the mutator context. + +``Res StackScan(ScanState ss, void *stackCold, mps_area_scan_t scan_area, void *closure)`` + +_`.if.scan`: Scan the stack of the current thread, between +``stackCold`` and the hot end of the mutator's stack that was recorded +by ``STACK_CONTEXT_SAVE()`` when the arena was entered. This will +include any roots which were in the mutator's callee-save registers on +entry to the MPS (see `.sol.setjmp`_ and `.sol.stack.nest`_). Return +``ResOK`` if successful, or another result code if not. + +_`.if.scan.begin-end`: This function must be called between +``STACK_CONTEXT_BEGIN()`` and ``STACK_CONTEXT_END()``. + +``STACK_CONTEXT_SAVE(StackContext sc)`` + +_`.if.save`: Store the mutator context in the structure ``sc``. + +_`.if.save.macro`: This must be implemented as a macro because it +needs to run in the stack frame of the entry point (if it runs in some +other function it does not necessarily get the mutator's registers). +This necessity to have the definition in scope in ``mpsi.c``, while +also having different definitions on different platforms, requires a +violation of design.mps.config.no-spaghetti_ in ss.h. + +.. _design.mps.config.no-spaghetti: config#no-spaghetti + +``STACK_CONTEXT_BEGIN(Arena arena)`` + +_`.if.begin`: Start an MPS operation that may need to know the mutator +context (see `.sol.entry-points`_). This macro must be used like this:: + + Res res; + ArenaEnter(arena); + STACK_CONTEXT_BEGIN(arena) { + res = ArenaStartCollect(...); + } STACK_CONTEXT_END(arena); + ArenaLeave(arena); + return res; + +That is, it must be paired with ``STACK_CONTEXT_END()``, and there +must be no ``return`` between the two macro invocations. + +This macro stores the mutator context in a ``StackContext`` structure +allocated on the stack, and sets ``arena->stackWarm`` to the hot end +of the current frame (using `.sol.stack.hot`_). + +``STACK_CONTEXT_END(Arena arena)`` + +_`.if.end`: Finish the MPS operation that was started by +``STACK_CONTEXT_BEGIN()``. + +This macro sets ``arena->stackWarm`` to ``NULL``. + + +Implementations +--------------- + +_`.impl`: Generic implementation of ``StackScan()`` in ``ss.c`` scans +the whole area between ``arena->stackWarm`` and the cold end of the +mutator's stack, implementing `.sol.stack.nest`_ and also the backup +strategy in `.sol.entry-points.fragile`_. + +.. figure:: stack-scan-areas.svg + :align: center + :alt: Diagram: scanned areas of the stack. + + +References +---------- + +.. [C1990] + International Standard ISO/IEC 9899:1990. "Programming languages — C". + +.. [C1999] + International Standard ISO/IEC 9899:1999. "`Programming languages — C `_". + +.. [Fog] + Agner Fog; + "`Calling conventions for different C++ compilers and operating systems `_"; + Copenhagen University College of Engineering; + 2014-08-07. + +.. [x86_64_registers] + Microsoft Corporation; + "`Caller/Callee Saved Registers `_". + + +Document History +---------------- + +- 2014-10-22 GDR_ Initial draft. + +- 2016-03-03 RB_ Reorganised based mostly on `.sol.stack.hot`_ and + `.sol.stack.nest`_. + +.. _GDR: http://www.ravenbrook.com/consultants/gdr/ +.. _RB: http://www.ravenbrook.com/consultants/rb/ + + +Copyright and License +--------------------- + +Copyright © 2014-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/mps/design/strategy.txt b/mps/design/strategy.txt index 6e0472b04f5..fe80fad7084 100644 --- a/mps/design/strategy.txt +++ b/mps/design/strategy.txt @@ -215,13 +215,14 @@ 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: +far from straightforward: see job003772_ and job004007_ 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 seven accounts: .. _job003772: http://www.ravenbrook.com/project/mps/issue/job003772/ +.. _job004007: http://www.ravenbrook.com/project/mps/issue/job004007/ _`.account.total`: Memory acquired from the arena. @@ -238,6 +239,10 @@ would complain. _`.account.free`: Memory that is not in use (free or lost to fragmentation). +_`.account.buffered`: Memory in a buffer that was handed out to the +client program via ``BufferFill()``, and which has not yet been +condemned. + _`.account.new`: Memory in use by the client program, allocated since the last time the generation was condemned. @@ -267,15 +272,19 @@ accounted as *old* or *oldDeferred* (see `.accounting.op.reclaim`_). Finally, debit *free*, credit *total*. (But see `.account.total.negated`_.) -_`.accounting.op.fill`: Allocate memory, for example by filling a -buffer. Debit *free*, credit *new* or *newDeferred*. +_`.accounting.op.fill`: Fill a buffer. Debit *free*, credit *buffered*. -_`.accounting.op.empty`: Deallocate memory, for example by emptying -the unused portion of a buffer. Debit *new* or *newDeferred*, credit -*free*. +_`.accounting.op.empty`: Empty a buffer. Debit *buffered*, credit +*new* or *newDeferred* with the allocated part of the buffer, credit +*free* with the unused part of the buffer. -_`.accounting.op.age`: Condemn memory. Debit *new* or *newDeferred*, -credit *old* or *oldDeferred*. +_`.accounting.op.age`: Condemn memory. Debit *buffered* (if part or +all of a buffer was condemned) and either *new* or *newDeferred*, +credit *old* or *oldDeferred*. Note that the condemned part of the +buffer remains part of the buffer until the buffer is emptied, but is +now accounted as *old* or *oldDeferred*. The uncondemned part of the +buffer, if any, remains accounted as *buffered* until it is either +emptied or condemned in its turn. _`.accounting.op.reclaim`: Reclaim dead memory. Debit *old* or *oldDeferred*, credit *free*. @@ -415,7 +424,7 @@ other uses of that: pool. Any non-white segment in the rampGen with new set to FALSE has its size added to ``poolGen->newSize`` and gets new set to TRUE. -- in ``AMCWhiten()``, if new is TRUE, the segment size is deducted +- in ``amcSegWhiten()``, if new is TRUE, the segment size is deducted from ``poolGen.newSize`` and new is set to FALSE. @@ -456,58 +465,65 @@ for example job003898_. Deciding whether to collect the world ..................................... -``Bool PolicyShouldCollectWorld(Arena arena, double interval, double multiplier, Clock now, Clock clocks_per_sec)`` +``Bool PolicyShouldCollectWorld(Arena arena, double availableTime, 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()``. +``TRUE`` if so, ``FALSE`` if not. The ``availableTime`` argument is an +estimate of the time that's available for the collection, ``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.) +_`.policy.world.impl`: There are two conditions: the 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 ................ -``Bool PolicyStartTrace(Trace *traceReturn, Arena arena)`` +``Bool PolicyStartTrace(Trace *traceReturn, Bool *collectWorldReturn, Arena arena, Bool collectWorldAllowed)`` _`.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 +_`.policy.start.world`: If ``collectWorldAllowed`` is TRUE, consider +starting a collection of the whole world, and if such a collection is +started, set ``*collectWorldReturn`` to TRUE. + +This decision 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]_. -_`.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. +_`.policy.start.world.hack`: The ``collectWorldAllowed`` flag was +added to fix job004011_ by ensuring that the MPS starts at most one +collection of the world in each call to ``ArenaPoll()``. But this is +is fragile and inelegant. Ideally the MPS would be able to deduce that +a collection of a set of generations can't possibly make progress +(because nothing that refers to this set of generations has changed), +and so not start such a collection. + +.. _job004011: http://www.ravenbrook.com/project/mps/issue/job004011/ + +_`.policy.start.chain`: If ``collectWorldAllowed`` is FALSE, or 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. - +condemn, and condemns all the segments in those generations. Trace progress @@ -518,16 +534,20 @@ Trace progress _`.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)`` +``Bool PolicyPollAgain(Arena arena, Clock start, Bool moreWork, Work tracedWork)`` _`.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()``. +clock time when the MPS was entered; ``moreWork`` and ``tracedWork`` +are the results of 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. +_`.policy.poll.impl`: The implementation keep doing work until either +the maximum pause time is exceeded (see `design.mps.arena.pause-time`_), +or there is no more work to do. Then it schedules the next collection +so that there is approximately one call to ``TracePoll()`` for every +``ArenaPollALLOCTIME`` bytes of allocation. + +.. _design.mps.arena.pause-time: arena#pause-time References @@ -556,8 +576,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2016 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2013-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 diff --git a/mps/design/telemetry.txt b/mps/design/telemetry.txt index c19bbab0056..33ad9a4e38d 100644 --- a/mps/design/telemetry.txt +++ b/mps/design/telemetry.txt @@ -457,8 +457,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +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 diff --git a/mps/design/tests.txt b/mps/design/tests.txt index e1e9eb51e83..47270217866 100644 --- a/mps/design/tests.txt +++ b/mps/design/tests.txt @@ -56,6 +56,91 @@ _`.test.zcoll`: Collection scheduling, and collection feedback. _`.test.zmess`: Message lifecycle and finalization messages. +Performance test +---------------- + +_`.test.ratio`: The ``testratio`` target checks that the hot variety +is not too much slower than the rash variety. A failure of this test +usually is expected to indicate that there are assertions on the +critical path using ``AVER`` instead of ``AVER_CRITICAL`` (and so on). +This works by running gcbench for the AMC pool class and djbench for +the MVFF pool class, in the hot variety and the rash variety, +computing the ratio of CPU time taken in the two varieties, and +testing that this falls under an acceptable limit. + +Note that we don't use the elapsed time (as reported by the benchmark) +because we want to be able to run this test on continuous integration +machines that might be heavily loaded. + +This target is currently supported only on Unix platforms using GNU +Makefiles. + + +Adding a new smoke test +----------------------- + +To add a new smoke test to the MPS, carry out the following steps. +(The procedure uses the name "smoketest" throughout but you should of +course replace this with the name of your test case.) + +_`.new.source`: Create a C source file in the code directory, +typically named "smoketest.c". In additional to the usual copyright +boilerplate, it should contain a call to ``testlib_init()`` (this +ensures reproducibility of pseudo-random numbers), and a ``printf()`` +reporting the absence of defects (this output is recognized by the +test runner):: + + #include + #include "testlib.h" + + int main(int argc, char *argv[]) + { + testlib_init(argc, argv); + /* test happens here */ + printf("%s: Conclusion: Failed to find any defects.\n", argv[0]); + return 0; + } + +_`.new.unix`: If the test case builds on the Unix platforms (FreeBSD, +Linux and macOS), edit code/comm.gmk adding the test case to the +``TEST_TARGETS`` macro, and adding a rule describing how to build it, +typically:: + + $(PFM)/$(VARIETY)/smoketest: $(PFM)/$(VARIETY)/smoketest.o \ + $(TESTLIBOBJ) $(PFM)/$(VARIETY)/mps.a + +_`.new.windows`: If the test case builds on Windows, edit +code/commpre.nmk adding the test case to the ``TEST_TARGETS`` macro, +and edit code/commpost.nmk adding a rule describing how to build it, +typically:: + + $(PFM)\$(VARIETY)\smoketest.exe: $(PFM)\$(VARIETY)\smoketest.obj \ + $(PFM)\$(VARIETY)\mps.lib $(FMTTESTOBJ) $(TESTLIBOBJ) + +_`.new.macos`: If the test case builds on macOS, open +code/mps.xcodeproj/project.pbxproj for edit and open this project in +Xcode. If the project navigator is not visible at the left, select +View → Navigators → Show Project Navigator (⌘1). Right click on the +Tests folder and choose Add Files to "mps"…. Select code/smoketest.c +and then click Add. Move the new file into alphabetical order in the +Tests folder. Click on "mps" at the top of the project navigator to +reveal the targets. Select a test target that is similar to the one +you have just created. Right click on that target and select Duplicate +(⌘D). Select the new target and change its name to "smoketest". Select +the "Build Phases" tab and check that "Dependencies" contains the mps +library, and that "Compile Sources" contains smoketest.c and +testlib.c. Close the project. + +_`.new.database`: Edit tool/testcases.txt and add the new test case to +the database. Use the appropriate flags to indicate the properties of +the test case. These flags are used by the test runner to select the +appropriate sets of test cases, for example tests marked ``=P`` +(Polling) can't be run on the protectionless build of the MPS. + +_`.new.manual`: Edit manual/source/code-index.rst and add the new test +case to the "Automated test cases" section. + + Document History ---------------- @@ -69,6 +154,8 @@ Document History - 2013-05-23 GDR_ Converted to reStructuredText. +- 2018-06-15 GDR_ Procedure for adding a new smoke test. + .. _RB: http://www.ravenbrook.com/consultants/rb/ .. _GDR: http://www.ravenbrook.com/consultants/gdr/ @@ -76,8 +163,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2016 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2013-2018 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 diff --git a/mps/design/testthr.txt b/mps/design/testthr.txt index a1bf99bb247..86f545aa194 100644 --- a/mps/design/testthr.txt +++ b/mps/design/testthr.txt @@ -115,8 +115,8 @@ Document History Copyright and License --------------------- -Copyright © 2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +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 diff --git a/mps/design/thread-manager.txt b/mps/design/thread-manager.txt index 88f2b2478fa..51fbaaecc8d 100644 --- a/mps/design/thread-manager.txt +++ b/mps/design/thread-manager.txt @@ -85,7 +85,7 @@ 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 +the documentation for ``mps_thread_reg()`` 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 @@ -117,7 +117,7 @@ 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. -.. _design.mps.sig.check.arg.unlocked: sig#check.arg.unlocked +.. _design.mps.sig.check.arg.unlocked: sig#check-arg-unlocked ``Arena ThreadArena(Thread thread)`` @@ -210,18 +210,18 @@ specially by the POSIX thread extensions. See design.mps.pthreadext.req.suspend.multiple_ and design.mps.pthreadext.req.resume.multiple_. -.. _design.mps.pthreadext.req.suspend.multiple: pthreadext#req.suspend.multiple -.. _design.mps.pthreadext.req.resume.multiple: pthreadext#req.resume.multiple +.. _design.mps.pthreadext.req.suspend.multiple: pthreadext#req-suspend-multiple +.. _design.mps.pthreadext.req.resume.multiple: pthreadext#req-resume-multiple _`.impl.ix.suspend`: ``ThreadRingSuspend()`` calls ``PThreadextSuspend()``. See design.mps.pthreadext.if.suspend_. -.. _design.mps.pthreadext.if.suspend: pthreadext#if.suspend +.. _design.mps.pthreadext.if.suspend: pthreadext#if-suspend _`.impl.ix.resume`: ``ThreadRingResume()`` calls ``PThreadextResume()``. See design.mps.pthreadext.if.resume_. -.. _design.mps.pthreadext.if.resume: pthreadext#if.resume +.. _design.mps.pthreadext.if.resume: pthreadext#if-resume _`.impl.ix.scan.current`: ``ThreadScan()`` calls ``StackScan()`` if the thread is current. @@ -279,8 +279,8 @@ _`.impl.w3.scan.suspended`: Otherwise, ``ThreadScan()`` calls pointer. -OS X implementation -................... +macOS implementation +.................... _`.impl.xc`: In ``thxc.c``. @@ -335,8 +335,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2013-2018 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 diff --git a/mps/design/thread-safety.txt b/mps/design/thread-safety.txt index 826d008a596..d64ee2b4f1e 100644 --- a/mps/design/thread-safety.txt +++ b/mps/design/thread-safety.txt @@ -6,7 +6,7 @@ Thread safety in the MPS :Tag: design.mps.thread-safety :Author: David Moore :Date: 1995-10-03 -:Status: incomplete design +:Status: complete design :Revision: $Id$ :Copyright: See `Copyright and License`_. :Index terms: pair: thread safety; design @@ -17,18 +17,14 @@ Introduction _`.intro`: This describes how thread safety is achieved in the MPS. - -Overview --------- - -_`.over`: The MPS is expected to run in an environment with multiple -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_) 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. +_`.overview`: The MPS is expected to run in an environment with +multiple 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_) 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 @@ -36,57 +32,27 @@ MPS and released on exit from it. So there is at most a single thread Requirements ------------ -_`.req.mt`: Code must work correctly in presence of multiple threads -all calling into the MPS. +_`.req.threads`: Code must work correctly in presence of multiple +threads all calling into the MPS. + +_`.req.arena`: The MPS must safely manage per-arena non-shared data. + +_`.req.global.mutable`: The MPS must safely manage global data that +may be updated many times (that is, the arena ring). + +_`.req.global.once`: The MPS must safely manage global data that is +updated at most once (that is, the protocol classes). + +_`.req.deadlock`: The MPS must not deadlock. + +_`.req.fork`: On Unix platforms, the MPS should be able to continue in +the child process after a ``fork()``. (Source: job004062_.) + +.. _job004062: https://www.ravenbrook.com/project/mps/issue/job004062/ _`.req.perf`: Performance should not be unreasonably hindered. -Architecture ------------- - -_`.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: 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: protocol#impl.init-lock - -_`.arch.other`: Other: data not shared. - -_`.arch.static`: Static data: sigs: shared-non-mutable always inited -to same thing. - -_`.arena-entry`: Each arena has a single lock. Externally visible -calls fall into two categories. Simple: arena lock not held. Lock is -claimed on entry, and released on exit. Recall: These are callable -only after a call-back from the MPS. In this case a arena lock is -already held. - -_`.interface`: The definition of the interface should guarantee safe -use of calls (from a locking point of view). For example, a buffer -must be exclusive to a thread. - -_`.buffers`: The buffer code is designed not to need a lock in the -fast case. A lock is only claimed on the exceptional reserve, trip and -commit cases (fill and trip?). A buffer contains references to shared -data (via pool field). Accessing this shared data must involve a lock. - -_`.deadlock`: A strict ordering is required between the global and -arena locks to prevent deadlock. The binary global lock may not be -claimed while either the arena or recursive global lock is held; the -arena lock may not be claimed while the recursive global lock is held. -Each arena lock is independent of all other arena locks; that is, a -thread may not attempt to claim more than one arena lock at a time. - - Analysis -------- @@ -94,6 +60,7 @@ _`.anal.simple`: To have the code functioning correctly it should be easy to change correctly. So a simple approach is desirable. We have to also ensure that performance is not unreasonably downgraded. + Performance cost of locking ........................... @@ -110,7 +77,7 @@ _`.lock-cost`: The cost of locking in performance terms are: _`.anal.perf.signif`: `.lock-cost.pause`_ is significant if there are MPS functions that take a long time. Using more locks, e.g. having a lock per pool as well as a lock per arena, is a way of decreasing the -locking conflict between threads (.lock-cost.pause and +locking conflict between threads (`.lock-cost.pause`_ and `.lock-cost.wait`_). However this could increase `.lock-cost.overhead`_ significantly. @@ -137,6 +104,7 @@ estimate, most significantly the overhead of calling the locking functions. Hence it would be undesirable from a performance point of view to have more than one lock. + Recursive vs binary locks ......................... @@ -168,169 +136,152 @@ easiest to implement first, but could be evolved into a `.binary`_ strategy. (That evolution has now happened. tony 1999-08-31). -Ideas ------ +Fork safety +........... -_`.sol.arena-lock`: Lock per arena which locks all MPS structures -associated with the arena, except allocation buffers. +In order to support ``fork()``, we need to solve the following problems: -_`.sol.init`: Shared static data may not be changed. It is initialised -before being read, and if re-initalised the values written must be -identical to those already there. Essentially only read-only shared -static data is allowed. +_`.anal.fork.lock`: Any MPS lock might be held by another thread at +the point where ``fork()`` is called. The lock would be protecting the +integrity of some data structure. But in the child the thread holding +the lock no longer exists, and so there is no way to restore the +integrity. -_`.sol.fine-grain`: Use finer grained locks, for example, a lock per -per pool instance. Arena lock locks only operations on arena. Pool -locks are claimed per pool. An ordering on pool instances would avoid -deadlock. +_`.anal.fork.threads`: In the child process after a ``fork()``, there +is only one thread, which is a copy of the thread that called +``fork()`` in the parent process. All other threads no longer exist. +But the MPS maintains references to these threads, via the +``ThreadStruct`` object` created by calls to ``mps_thread_reg()``. If +we try to communicate with these threads it will fail or crash. -_`.sol.global`: Use global locks for genuinely global data which must -be updated dynamically. An ordering between global and arena locks -would avoid deadlock. +_`.anal.fork.exc-thread`: On macOS, the MPS handles protection faults +using a dedicated thread. But in the child process after a ``fork()``, +this dedicated thread no longer exists. Also, the Mach port on which +the dedicated thread receives its messages does not exist in the child +either. + +_`.anal.fork.mach-port`: On macOS, the MPS identifies threads via +their Mach port numbers, which are stashed in the ``ThreadStruct`` and +used to identify the current thread, for example in +``ThreadSuspend()``. But in the child process after ``fork()`` the +running thread has a different Mach port number than it did in the +parent. -Implementation --------------- +Design +------ -Use MPS locks (design.mps.lock_) to do locking. +_`.sol.locks`: Use MPS locks (design.mps.lock_) to implement the +locking. -Locking Functions -................. +.. _design.mps.lock: lock -``ArenaEnter()`` and ``ArenaLeave()`` are used to claim and release the -arena lock. To implement this: +_`.sol.arena`: Each arena has a binary lock that protects the +non-shared data for that arena. Functions in the public interface fall +into the following categories: -- There is a lock for every arena. The arena class init function - allocates the lock as well as the arena itself. -- ``ArenaInit()`` calls ``LockInit()`` on the lock and initializes the - pointer to it from the arena. -- ``ArenaDestroy()`` calls ``LockFinish()`` on it. -- ``ArenaEnter()`` claims the lock. -- ``ArenaLeave()`` releases the lock. +- _`.sol.arena.entry`: Must be called with the arena lock not held + (thus, these functions are not callable from format methods and + other callbacks). Claims arena binary lock on entry, releases it on + exit. The usual case. For example, ``mps_arena_park()``. -Shared and non-shared data -.......................... +- _`.sol.arena.recursive`: May be called with the arena lock held (for + example, from format methods and other callbacks). Claim arena lock + recursively on entry, release it on exit. For example, + ``mps_addr_fmt()``. -Non-shared data is data for which no other thread has a handle on it. -shared-non-mutable data is data which is never changed after -initialisation. It may be re-initialised, if re-initialisation does -not change its value. atomically updatable data is data which is not -locked, but may be shared because it is in a consistent state before -and after an update. +- _`.sol.arena.lock-free`: May be called at any time and does not + claim or release any locks, because it is documented as being up to + the client program to ensure thread safety (for example, + ``mps_ld_add()``). -A function is "safe" if it may safely execute without exclusive access -to the data it manipulates. +- _`.sol.arena.maybe-entry`: Must be called with the arena lock not + held. In the common case, does not claim or release any locks + (because it is documented as being up to the client program to + ensure thread safety, as for `.sol.arena.lock-free`_), but may need + to claim and release the arena binary lock (as for + `.sol.arena.entry`_). For example, ``mps_reserve()``, + ``mps_commit()``, ``mps_ap_frame_push()``, and + ``mps_ap_frame_pop()``. -A "safe" function may: +_`.sol.global.mutable`: There is a global binary lock (see +design.mps.lock.req.global.binary_) that protects mutable data shared +between all arenas (that is, the arena ring lock: see +design.mps.arena.static.ring.lock_). -- call other safe functions; -- manipulate non-shared data; -- read shared-non-mutable data; -- claim the arena lock around code which may manipulate shared data in - the arena. +.. _design.mps.lock.req.global.binary: lock#req-global-binary +.. _design.mps.arena.static.ring.lock: arena#static-ring-lock -Each function in the external MPS interface falls into one of the -following categories: +_`.sol.global.once`: There is a global recursive lock (see +design.mps.lock.req.global.recursive_) that protects static data which +must be initialized at most once (that is, the protocol classes). Each +static data structure is accessed only via an "ensure" function that +claims the global recursive lock, checks to see if the data structure +has been initialized yet, and does so if necessary (see +design.mps.protocol.impl.define-class.lock_). -- calls ``ArenaEnter()`` on entry and ``ArenaLeave()`` on exit; -- uses ``PoolArena()`` to identify the arena, before claiming the lock; -- uses ``BufferPool()`` and ``PoolArena()`` to identify the arena, before - claiming the lock; -- is not defined as external but is listed for explicitness; -- only claims the lock in otherwise unsafe situations (buffer code?); -- may be called externally but only in a situation where the arena - lock is already held; -- is the unique accessor of its data. +.. _design.mps.lock.req.global.recursive: lock#req-global-recursive +.. _design.mps.protocol.impl.define-class.lock: protocol#impl-define-class-lock -So ``PoolArena()`` and ``BufferPool()`` must be "safe". ``pool->arena`` is -shared-non-mutable. ``buffer->pool`` is shared-non-mutable. +_`.sol.deadlock`: A strict ordering is required between the global and +arena locks to prevent deadlock. The binary global lock may not be +claimed while either the arena or recursive global lock is held; the +arena lock may not be claimed while the recursive global lock is held. +Each arena lock is independent of all other arena locks; that is, a +thread may not attempt to claim more than one arena lock at a time. +See design.mps.arena.lock.avoid_. -Validation -.......... +.. _design.mps.arena.lock.avoid: arena#lock-avoid -We have to be careful about validation. Any function that is called -from a arena-safe function without the arena-lock held, must itself be -safe, or manipulating non-shared data. +_`.sol.check`: The MPS interface design requires that a function must +check the signatures on the data structures pointed to by its +parameters (see design.mps.sig.check.arg_). In particular, for +functions in the class `.sol.arena.entry`_ it is necessary to check +some data structure signatures before taking the arena lock. The +checking interface provides a ``TESTT()`` macro that checks the +signature in a thread-safe way (see +design.mps.sig.check.arg.unlocked_). -For example, calling ``PoolIsValid()`` before claiming the lock would be -wrong if ``PoolIsValid()`` is unsafe. Defining it to be safe would -involve locking it, which if done in all similar situations would be -very expensive. - -Possibly remove validation from accessor methods; replace with -signature check and ``IsValid()`` calls in callers of accessor -functions. - -Annotations?: -- safe -- non-shared -- shared-non-mutable - -Safe functions -.............. - -Arena - -- ``ArenaCreate()`` -- no shared data; no lock; calls ``LockInit()``. -- ``ArenaDestroy()`` -- no shared data; no lock (should only finish - arena after use); calls ``LockFinish()``. -- ``ArenaDescribe()`` -- lock. - -Root (for the purposes of locking this module can be thought of as external) - -- ``RootCreate()`` -- calls create -- ``RootCreateTable()`` -- calls create -- create -- lock -- ``RootDestroy()`` -- lock -- ``RootDescribe()`` -- lock - -will be attached to arena, can lock now. +.. _design.mps.sig.check.arg: sig#check-arg +.. _design.mps.sig.check.arg.unlocked: sig#check-arg-unlocked -Pool +Fork safety +----------- -- ``PoolCreate()`` / ``PoolCreateV()`` -- lock (Create calls CreateV which locks). -- ``PoolDestroy()`` -- lock -- ``PoolAlloc()`` -- lock -- ``PoolFree()`` -- lock -- ``PoolArena()`` -- accesses shared-non-mutable data only -- ``PoolDescribe()`` -- lock +_`.sol.fork.atfork`: The MPS solves the fork-safety problems by +calling |pthread_atfork|_ to install handler functions that are +called in the parent process just before fork (the "prepare" handler), +and in the parent and child processes just after fork (the "parent" +and "child" handlers respectively). -Format +.. |pthread_atfork| replace:: ``pthread_atfork()`` +.. _pthread_atfork: http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_atfork.html -- ``FormatCreate()`` -- lock -- ``FormatDestroy()`` -- lock +_`.sol.fork.lock`: In the prepare handler, the MPS takes all the +locks: that is, the global locks, and then the arena lock for every +arena. Note that a side-effect of this is that the shield is entered +for each arena. In the parent handler, the MPS releases all the locks. +In the child handler, the MPS would like to release the locks but this +does not work on any supported platform, so instead it reinitializes +them, by calling ``LockInitGlobal()``. -Buffer +_`.sol.fork.thread`: On macOS, in the prepare handler, the MPS +identifies for each arena the current thread, that is, the one calling +``fork()`` which will survive into the child process, and marks this +thread by setting a flag in the appropriate ``ThreadStruct``. In the +parent handler, this flag is cleared. On all Unix platforms, in the +child handler, all threads (except for the current thread) are marked +as dead and transferred to the ring of dead threads. (The MPS can't +destroy the thread structures at this point because they are owned by +the client program.) -- ``BufferCreate()`` -- lock -- ``BufferDestroy()`` -- lock -- ``BufferFill()`` -- lock -- ``BufferTrip()`` -- lock -- ``BufferPool()`` -- accesses shared-non-mutable data only -- ``BufferDescribe()`` -- lock -- ``BufferCommit()`` -- "unsafe": buffer may be used by single thread - only. (but safe wrt arena) -- ``BufferReserve()`` -- "unsafe": also +_`.sol.fork.exc-thread`: On macOS, in the child handler, the exception +port and dedicated thread are re-created, and the current thread +re-registered with the exception port. -PoolClass (only shared data is static and non-mutable) - -- ``PoolClass()`` -- ``PoolClassAMC()`` -- ``PoolClassMV()`` -- ``PoolClassMFS()`` - -Sig (as with ``PoolClass``, relies on static data reinitialised to -constant value) - -Collect - -- ``Collect()`` -- lock - -Thread - -- ``ThreadRegister()`` -- lock -- ``ThreadDeregister()`` -- lock +_`.sol.fork.mach-port`: On macOS, in the child handler, the thread +flagged as forking gets its port number updated. Document History @@ -342,6 +293,8 @@ Document History - 2013-05-22 GDR_ Converted to reStructuredText. +- 2018-06-14 GDR_ Added fork safety design. + .. _RB: http://www.ravenbrook.com/consultants/rb/ .. _GDR: http://www.ravenbrook.com/consultants/gdr/ @@ -349,8 +302,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2013-2018 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 diff --git a/mps/design/trace.txt b/mps/design/trace.txt index 236a0cae5d9..3d17b1c865c 100644 --- a/mps/design/trace.txt +++ b/mps/design/trace.txt @@ -138,15 +138,13 @@ 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. +whether the reference is to a white segment. .. 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_. + 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/ @@ -157,14 +155,14 @@ the branch pedictors) resulting in a slow down. Replacing the 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) -and in ``dylan_copy`` a call to ``dylan_skip`` (to recompute the -length) and call to ``memcpy`` with general parameters. Replacing this -with a direct call to ``memcpy`` removes these overheads and the call -to ``memcpy`` now has aligned parameters. The call to ``memcpy`` is -inlined by the C compiler. This change results in a 4–5% speed-up in -the Dylan compiler. +_`.fix.nocopy`: ``amcSegFix()`` used to copy objects by using the +format's copy method. This involved a function call (through an +indirection) and in ``dylan_copy`` a call to ``dylan_skip`` (to +recompute the length) and call to ``memcpy`` with general parameters. +Replacing this with a direct call to ``memcpy`` removes these +overheads and the call to ``memcpy`` now has aligned parameters. The +call to ``memcpy`` is 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 @@ -295,8 +293,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +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 diff --git a/mps/design/type.txt b/mps/design/type.txt index 25943f41a4b..89b43be9515 100644 --- a/mps/design/type.txt +++ b/mps/design/type.txt @@ -97,8 +97,6 @@ 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. @@ -108,9 +106,10 @@ Attribute Description =================== =================================================== There is an attribute field in the pool class (``PoolClassStruct``) -which declares the attributes of that class. See design.mps.class-interface.field.attr_. +which declares the attributes of that class. See +design.mps.pool.field.attr_. -.. _design.mps.class-interface.field.attr: class-interface +.. _design.mps.pool.field.attr: pool#field-attr ``typedef int Bool`` @@ -277,29 +276,6 @@ Value Description ==================== ================================================== -``typedef unsigned FrameState`` - -_`.framestate`: ``FrameState`` represents the current state in a -buffer frame's lifecycle. See design.mps.alloc-frame_. It takes one of -the following values: - -.. _design.mps.alloc-frame: alloc-frame - -========================== ============================================ -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 void (*Fun)(void)`` _`.fun`: ``Fun`` is the type of a pointer to a function about which @@ -323,9 +299,9 @@ determined then the smallest unsigned integer with a large enough range may be used instead. -``typedef int LocusPrefKind`` +``typedef unsigned LocusPrefKind`` -_`.segprefkind`: The type ``LocusPrefKind`` expresses a preference for +_`.locusprefkind`: The type ``LocusPrefKind`` expresses a preference for addresses within an address space. It takes one of the following values: @@ -488,19 +464,18 @@ Root mode Description ============================= ========================================= _`.rootmode.const.unused`: ``RootModeCONSTANT`` has no effect. This -mode was introduced in the hope of being able to maintain a -:term:`remembered set` for the root without needing a :term:`write -barrier`, but it can't work as described, since you can't reliably -create a valid registered constant root that contains any references. -(If you add the references before registering the root, they may have -become invalid; but you can't add them afterwards because the root is -supposed to be constant.) +mode was introduced in the hope of being able to maintain a remembered +set for the root without needing a write barrier, but it can't work as +described, since you can't reliably create a valid registered constant +root that contains any references. (If you add the references before +registering the root, they may have become invalid; but you can't add +them afterwards because the root is supposed to be constant.) _`.rootmode.conv.c`: ``RootMode`` is converted to ``mps_rm_t`` in the MPS C Interface. -``typedef int RootVar`` +``typedef unsigned RootVar`` _`.rootvar`: The type ``RootVar`` is the type of the discriminator for the union within ``RootStruct``. @@ -576,7 +551,7 @@ _`.traceid`: A ``TraceId`` is an unsigned integer which is less than 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 +.. _design.mps.trace.instance.limit: trace#instance-limit ``typedef unsigned TraceSet`` @@ -586,7 +561,9 @@ represented in the obvious way:: member(ti, ts) ⇔ ((1<. This is an open source license. Contact +Copyright © 2013-2018 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 diff --git a/mps/design/version-library.txt b/mps/design/version-library.txt index b679c382713..292908f6703 100644 --- a/mps/design/version-library.txt +++ b/mps/design/version-library.txt @@ -6,7 +6,7 @@ Library version mechanism :Tag: design.mps.version-library :Author: David Jones :Date: 1998-08-19 -:Status: incomplete document +:Status: complete document :Revision: $Id$ :Copyright: See `Copyright and License`_. :Index terms: pair: library version mechanism; design @@ -38,14 +38,12 @@ release of the MM one is using. Overview -------- -_`.overview`: See design.mps.version for discussion and design of -versions of other aspects of the software. This document concentrates -on a design for determining which version of the library one has -linked with. There are two aspects to the design, allowing humans to -determine the version of an MPS library, and allowing programs to -determine the version of an MPS library. Only the former is currently -designed (a method for humans to determine which version of an MPS -library is being used). +_`.overview`: This is the design for determining which version of the +library one has linked against. There are two aspects to the design, +allowing humans to determine the version of an MPS library, and +allowing programs to determine the version of an MPS library. Only the +former is currently designed (a method for humans to determine which +version of an MPS library is being used). _`.overview.impl`: The overall design is to have a distinctive string compiled into the library binary. Various programs and tools will be @@ -56,12 +54,12 @@ the version of the MPS begin used. Architecture ------------ -_`.arch.structure`: The design consists of three components: +_`.arch.structure`: The design consists of two components: #. _`.arch.string`: A string embedded into any delivered library binaries (which will encode the necessary information). -#. _`.arch.proc`: A process by which the string is modified +#. _`.arch.proc`: Procedures by which the string is modified appropriately whenever releases are made. #. _`.arch.tool`: A tool and its documentation (it is expected that @@ -69,10 +67,6 @@ _`.arch.structure`: The design consists of three components: version string from a delivered library or an executable linked with the library. -_`.arch.not-here`: Only the string component -(arch.string) is directly described here. The other -components are described elsewhere. (where?) - The string will contain information to identify the following items: #. _`.arch.string.platform`: the platform being used. @@ -87,9 +81,10 @@ The string will contain information to identify the following items: Implementation -------------- -_`.impl.file`: The string itself is a declared C object in the file -``version.c`` (impl.c.version). It consists of a concatenation of -various strings which are defined in other modules. +_`.impl.file`: The version string itself is a declared C object +``MPSVersionString`` in the file ``version.c`` (impl.c.version). It +consists of a concatenation of various strings which are defined in +other modules. _`.impl.variety`: The string containing the name of the variety is the expansion of the macro ``MPS_VARIETY_STRING`` defined by ``config.h`` @@ -97,7 +92,10 @@ expansion of the macro ``MPS_VARIETY_STRING`` defined by ``config.h`` _`.impl.product`: The string containing the name of the product is the expansion of the macro ``MPS_PROD_STRING`` defined by ``config.h`` -(impl.h.config). +(impl.h.config). Note that there is now only one product, so this is +always ``"mps"`` (see design.mps.config.req.prod_). + +.. _design.mps.config.req.prod: config#req-prod _`.impl.platform`: The string containing the name of the platform is the expansion of the macro ``MPS_PF_STRING`` defined by ``mpstd.h`` @@ -111,10 +109,28 @@ _`.impl.version`: The string contains the version and release of the product. This is by the expansion of the macro ``MPS_RELEASE`` which is defined in this module (``version.c``). -_`.impl.usage`: To make a release, the ``MPS_RELEASE`` macro (see -impl.c.version.release) is edited to contain the release name (for -example, ``"release.epcore.brisling"``), and then changed back -immediately after the release checkpoint is made. +_`.impl.proc`: The ``MPS_RELEASE`` macro (see impl.c.version.release) +is edited after making a release so that it contains the name of the +next release to be made from the sources on that branch. For example, after +making version 1.117, the source on the master branch is updated to say:: + + #define MPS_RELEASE "release/1.118.0" + +and after making release 1.117.0, the source on the version/1.117 branch is updated to say:: + + #define MPS_RELEASE "release/1.117.1" + +See the version creation and release build procedures respectively. + +_`.impl.tool`: The version string starts with the characters +``"@(#)"``. This is recognized by the standard Unix utility |what|_. For example:: + + $ what mps.a + mps.a + Ravenbrook MPS, product.mps, release/1.117.0, platform.xci6ll, variety.asserted.logging.nonstats, compiled on Oct 18 2016 13:57:08 + +.. |what| replace:: ``what(1)`` +.. _what: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/what.html Document History @@ -133,8 +149,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2013-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 diff --git a/mps/design/version.txt b/mps/design/version.txt deleted file mode 100644 index 798c01ead99..00000000000 --- a/mps/design/version.txt +++ /dev/null @@ -1,101 +0,0 @@ -.. mode: -*- rst -*- - -Software versions -================= - -:Tag: design.mps.version -:Author: David Jones -:Date: 1998-08-19 -:Status: incomplete document -:Revision: $Id$ -:Copyright: See `Copyright and License`_. -:Index terms: - pair: software versions; design - single: versions; design - - -Introduction ------------- - -_`.intro`: This is the design of the support in the MPS for -describing and inspecting versions. - - -Overview --------- - -_`.overview`: There are three different sorts of version under -consideration: - -#. versions of the (MPS) library used (linked with); - -#. versions of the interface used (header files in C) when compiling - the client's program; and - -#. versions of the documentation used when the client was writing the - program. - -There are issues of programmatic and human access to these versions. - -_`.overview.split`: The design is split accordingly. See -design.mps.version-library_ for the design of a system for -determining the version of the library one is using. And other -non-existent documents for the others. - -.. _design.mps.version-library: version-library - - -Document History ----------------- - -- 1998-08-19 David Jones. Incomplete document. - -- 2002-06-07 RB_ Converted from MMInfo database design document. - -- 2013-03-11 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/mps/design/vm.txt b/mps/design/vm.txt index fb1be6eb5da..ef65ffffd0c 100644 --- a/mps/design/vm.txt +++ b/mps/design/vm.txt @@ -38,7 +38,7 @@ _`.req.granularity`: The virtual mapping module must report the necessary for the arena to be able to portably determine its grain size; see design.mps.arena.def.grain_.) -.. _design.mps.arena.def.grain: arena#def.grain +.. _design.mps.arena.def.grain: arena#def-grain _`.req.reserve`: The *reserve* operation must reserves a chunk of address space. @@ -270,7 +270,7 @@ passing ``PROT_NONE`` and ``MAP_PRIVATE | MAP_ANON``. _`.impl.ix.anon.trans`: Note that ``MAP_ANON`` ("map anonymous memory not associated with any specific file") is an extension to -POSIX, but it is supported by FreeBSD, Linux, and OS X. A work-around +POSIX, but it is supported by FreeBSD, Linux, and macOS. A work-around that was formerly used on systems lacking ``MAP_ANON`` was to map the file ``/dev/zero``. @@ -369,8 +369,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2013-2018 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 diff --git a/mps/design/vmo1.txt b/mps/design/vmo1.txt deleted file mode 100644 index 33c90858439..00000000000 --- a/mps/design/vmo1.txt +++ /dev/null @@ -1,112 +0,0 @@ -.. mode: -*- rst -*- - -VM for Digital Unix -=================== - -:Tag: design.mps.vmo1 -:Author: David Jones -:Date: 1997-03-25 -:Status: incomplete document -:Revision: $Id$ -:Copyright: See `Copyright and License`_. -:Index terms: pair: VM for Digital Unix; design - - -.. warning:: - - As of 2013-05-26, the MPS is no longer supported on Digital Unix, - so this document is only of historical interest. - - -Introduction ------------- - -_`.readership`: Any MPS developer. - -_`.intro`: This is the design of the VM Module for Digital UNIX (also -known as OSF/1 and Tru64 Unix; see os.o1). In general aspects -(including interface) the design is as for design.mps.vm_. - -.. _design.mps.vm: vm - - -Functions ---------- - -_`.fun.unmap`: ``VMUnmap()`` "unmaps" a region by replacing the -existing mapping with a mapping using the ``vm->none_fd`` file -descriptor (see mumble mumble, ``VMCreate()``), and protection set to -``PROT_NONE`` (that is, no access). - -_`.fun.unmap.justify`: Replacing the mapping in this way means that -the address space is still reserved and will not be used by calls to -``mmap()`` (perhaps in other libraries) which specify -``MAP_VARIABLE``. - -_`.fun.unmap.offset`: The offset for this mapping is the offset of the -region being unmapped in the VM; this gives the same effect as if -there was one mapping of the ``vm->none_fd`` from the base to the -limit of the VM (but "behind" all the other mappings that have been -created). - -_`.fun.unmap.offset.justify`: If this is not done (if for example the -offset is always specified as 0) then the VM will cause the kernel to -create a new file reference for each mapping created with -``VMUnmap()``; eventually the kernel refuses the ``mmap()`` call because it -can't create a new file reference. - - -Document History ----------------- - -- 1997-03-25 David Jones. Incomplete document. - -- 2002-06-07 RB_ Converted from MMInfo database design document. - -- 2013-05-26 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/mps/design/vmso.txt b/mps/design/vmso.txt deleted file mode 100644 index a73a0d65cbd..00000000000 --- a/mps/design/vmso.txt +++ /dev/null @@ -1,199 +0,0 @@ -.. mode: -*- rst -*- - -VM for Solaris -============== - -:Tag: design.mps.vmso -:Author: David Jones -:Date: 1998-05-08 -:Status: incomplete document -:Revision: $Id$ -:Copyright: See `Copyright and License`_. -:Index terms: pair: VM for Solaris; design - -.. warning:: - - As of 2013-05-26, the MPS is no longer supported on Solaris, so - this document is only of historical interest. - - -Introduction ------------- - -_`.intro`: This is the design for the VM implementation on Solaris 2.x -(see os.so for OS details). The implementation is in MMsrc!vmso.c -(impl.c.vm). The design follows the design for and implements the -contract of the generic VM interface (design.mps.vm_). To summarize: -The VM module provides a mechanism to reserve large (relative to the -amount of RAM) amounts of address space, and functions to map (back -with RAM) and unmap portions of this address space. - -.. _design.mps.vm: vm - -_`.source`: Much of the implementation (and hence the design) was -inherited from the SunOS4 implementation. Not that there's any design -for that. You'll find the ``mmap(2)`` (for the system call ``mmap()``) -and the ``zero(7d)`` (for the device ``/dev/zero``) man pages useful -as well. The generic interface and some generic design is in -design.mps.vm_. - - -Definitions ------------ - -_`.def`: See design.mps.vm.def_ for definitions common to all VMs. - -.. _design.mps.vm.def: vm#def - - -Overview --------- - -_`.over`: The system calls ``mmap()`` and ``munmap()`` are used to -access the underlying functionality. They are used in slightly unusual -ways, typically to overcome baroque features or implementation details -of the operating system. - -_`.over.reserve`: In order to reserve address space, a mapping to a -file (``/etc/passwd`` as it happens) is created with no protection -allowed. - -_`.over.map`: In order to map memory, a mapping to ``/dev/zero`` is -created. - -_`.over.destroy`: When the VM is destroyed, ``munmap()`` is used to -remove all the mappings previously created. - - -Implementation --------------- - -_`.impl.create`: ``VMCreate()`` - -_`.impl.create.vmstruct`: Enough pages to hold the ``VMStruct`` are -allocated by creating a mapping to ``/dev/zero`` (a read/write private -mapping), and using initializing the memory as a ``VMStruct``. - -_`.impl.create.reserve`: The size parameter is rounded up to page size -and this amount of address space is reserved. The address space is -reserved by creating a shared mapping to ``/etc/passwd`` with no -access allowed (the ``prot`` argument is ``PROT_NONE``, and the -``flags`` argument is ``MAP_SHARED``). - -_`.impl.create.reserve.mmap.justify`: ``mmap()`` gives us a flexible -way to allocate address space without interfering with any other -component in the process. Because we don't specify ``MAP_FIXED`` we -are guaranteed to get a range of addresses that are not in use. Other -components must cooperate by not attempting to create mappings -specifying ``MAP_FIXED`` and an address in the range that the MPS has -reserved. - -_`.impl.create.reserve.passwd.justify`: Mapping ``/etc/passwd`` like -this worked on SunOS 4 (so this implementation inherited it). Mapping -``/dev/zero`` with ``prot=PROT_NONE`` and ``flags=MAP_PRIVATE`` does -not work because Solaris gratuitously allocates swap (even though you -can't use the memory). - -_`.impl.create.reserve.improve`: However, it would appears that or-ing -in ``MAP_NORESERVE`` mapping ``/dev/zero`` will reserve address space -without allocating swap, so this might be worth trying. That is, with -``prot=PROT_NONE`` and ``flags=MAP_PRIVATE|MAP_NORESERVE``. However -the following caveat comes from the original implementation: -"Experiments have shown that attempting to reserve address space by -mapping ``/dev/zero`` results in swap being reserved. This appears to -be a bug, so we work round it by using ``/etc/passwd``, the only file -we can think of which is pretty much guaranteed to be around." So that -might not work after all. - -_`.impl.map`: ``VMMap()`` - -_`.impl.map.zero`: A mapping to ``/dev/zero`` is created at the -relevant addresses (overriding the map to ``/etc/passwd`` that was -previously in place for those addresses). The ``prot`` argument is -specified as ``PROT_READ|PROT_WRITE|PROT_EXEC`` (so that any access is -allowed), the ``flags`` argument as ``MAP_PRIVATE|MAP_FIXED``. The -flag ``MAP_PRIVATE`` means that the mapping is not shared with child -processes (child processes will have a mapping, but changes to the -memory will not be shared). The flag ``MAP_FIXED`` guarantees that we -get the mapping at the specified address). The ``zero(7d)`` man page -documents this as a way to create a "zero-initialized unnamed memory -object". - -_`.impl.map.error`: If there's not enough swap space for the mapping, -``mmap()`` will return ``EAGAIN``, not ``ENOMEM``, although you might -not think so from the man page. - -_`.impl.unmap`: ``VMUnmap()`` - -_`.impl.unmap.reserve`: The relevant addresses are returned to the -reserved state by creating a mapping to ``/etc/passwd`` (overriding -the map ``/dev/zero`` that was previously in place for those -addresses). As for ``VMCreate()`` (see `.impl.create.reserve`_ above) -the ``prot`` argument is ``PROT_NONE``, but the ``flags`` argument has -the addition ``MAP_FIXED`` flags (so is ``MAP_SHARED|MAP_FIXED``). - -_`.impl.unmap.reserve.offset`: The offset argument is specified to be -the offset of the addresses being unmapped from the base of the -reserved VM area. - -_`.impl.unmap.reserve.offset.justify`: Not specifying the offset like -this makes Solaris create a separate mapping (in the kernel) each time -Unmap is used, eventually the call to ``mmap()`` will fail. Specifying -offset like this does not cause Solaris to create any extra mappings, -the existing mapping to ``/etc/passwd`` gets reused. - - -Document History ----------------- - -- 1998-05-08 David Jones. Incomplete document. - -- 2002-06-07 RB_ Converted from MMInfo database design document. - -- 2013-05-26 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/mps/design/write-barrier.txt b/mps/design/write-barrier.txt new file mode 100644 index 00000000000..6c473d057a3 --- /dev/null +++ b/mps/design/write-barrier.txt @@ -0,0 +1,178 @@ +.. mode: -*- rst -*- + +Write barrier +============= + +:Tag: design.mps.write-barrier +:Author: Richard Brooksby +:Date: 2016-03-18 +:Status: incomplete design +:Revision: $Id$ +:Copyright: See `Copyright and License`_. +:Index terms: pair: write barrier; design + + +Introduction +------------ + +_`.intro`: This document explains the design of the write barrer of the +Memory Pool System (MPS). + +_`.readership`: This document is intended for developers of the MPS. + + +Overview +-------- + +_`.overview`: The MPS uses a combination of hardware memory protection +and BIBOP techniques to maintain an approximate remembered set. The +remembered set keeps track of areas of memory that refer to each +other, so that the MPS can avoid scanning areas that are irrelevant +during a garbage collection. The MPS write barrier is implemented by +a one-word "summary" of the zones referenced by a segment. That +summary can be compared with the "white set" of a trace by a simple +logical AND operation. + + +Write Barrier Processes +----------------------- + +_`.scan.summary`: As the MPS scans a segment during garbage collection, +it accumulates a summary of references. This summary is represented +by single word ``ZoneSet``, derived from the bit patterns of the +references. After the scan the MPS can decide to store the summary +with the segment, and use it in future garbage collections to avoid +future scans. + +If the summary does not intersect any of the zones containing +condemned objects, the MPS does not have to scan them in order to +determine if those objects are live. + +The mutator could update the references in a segment and make the +summary invalid. To avoid this, when the MPS stores a summary, it +raises a write barrier on the segment memory. If the mutator does +update the segment, the barrier is hit, and the MPS resets the +summary, so that the segment will be scanned in future. + + +[At this point I was interrupted by a man from Porlock.] + + +Write barrier deferral +---------------------- + +_`.deferral`: Both scanning and the write barrier cost CPU time, and +these must be balanced. There is no point spending 1000 CPU units +raising a write barrier to avoid 10 CPU units of scanning cost. +Therefore we do not raise the write barrier immediately. + +_`.deferral.heuristic`: We apply a simple heuristic: A segment which was +found to be "interesting" while scanning is likely to be interesting +again, and so raising the write barrier is not worthwhile. If we scan +a segment several times and find it "boring" then we raise the barrier +to avoid future boring scans. + +_`.def.boring`: A scan is "boring" if it was unnecessary for a garbage +collection because it found no references to condemned objects. + +_`.def.interesting`: A scan is "interesting" if it was not boring +(`.def.boring`_). Note that this does not mean it preserved comdemned +objects, only that we would have scanned it even if we had had the +scan summary beforehand. + +_`.deferral.count`: We store a deferral count with the segment. The +count is decremented after each boring scan (`.def.boring`_). The write +barrier is raised only when the count reaches zero. + +_`.deferral.reset`: The count is reset after three events: + + 1. segment creation (``WB_DEFER_INIT``) + + 2. an interesting scan (``WB_DEFER_DELAY``) + + 3. a barrier hit (``WB_DEFER_HIT``) + +_`.deferral.dabble`: The set of objects condemend by the garbage +collector changes, and so does what is interesting or boring. For +example, a collection of a nursery space in zone 3 might be followed +by a collection of a top generation in zone 7. This will upset +`.deferral.heuristic`_ somewhat. We assume that the garbage collector +will spend most of its time repeatedly collecting the same zones. + + +Improvements +------------ + +_`.improv.by-os`: The overheads hardware barriers varies widely between +operating systems. On Windows it is very cheap to change memory +protection and to handle protecion faults. On macOS it is very +expensive. The balance between barriers and scanning work is +different. We should measure the relative costs and tune the deferral +for each separately. + +_`.improv.balance`: Hardware costs of write barriers vary by OS, but +scanning costs vary depending on many factors including client code. +The MPS could dynamically measure these costs, perhaps using fast +cycle counters such as RDTSC, and use this to dynamically balance the +write barrier deferral. + + +References +---------- + +.. [job003975] "Poor performance due to imbalance between protection + and scanning costs"; Richard Brooksby; Ravenbrook + Limited; 2016-03-11; + . + + +Document History +---------------- + +- 2016-03-19 RB_ Created during preparation of + branch/2016-03-13/defer-write-barrier for [job003975]_. + +.. _RB: http://www.ravenbrook.com/consultants/rb/ + + +Copyright and License +--------------------- + +Copyright © 2016-2018 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/mps/design/writef.txt b/mps/design/writef.txt index 2a0277990e0..71792920102 100644 --- a/mps/design/writef.txt +++ b/mps/design/writef.txt @@ -158,8 +158,8 @@ Document History Copyright and License --------------------- -Copyright © 2013-2015 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 2013-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 diff --git a/mps/example/scheme/.p4ignore b/mps/example/scheme/.p4ignore index bf0e12d6afd..e368d2c9887 100644 --- a/mps/example/scheme/.p4ignore +++ b/mps/example/scheme/.p4ignore @@ -6,4 +6,7 @@ scheme-advanced.dSYM scheme-boehm scheme-boehm.dSYM scheme-malloc -scheme-malloc.dSYM \ No newline at end of file +scheme-malloc.dSYM +a.out +*.o +core diff --git a/mps/example/scheme/.renamed-gitignore b/mps/example/scheme/.renamed-gitignore new file mode 120000 index 00000000000..c5c99a6a89c --- /dev/null +++ b/mps/example/scheme/.renamed-gitignore @@ -0,0 +1 @@ +.p4ignore \ No newline at end of file diff --git a/mps/example/scheme/r4rs.scm b/mps/example/scheme/r4rs.scm index 251550a4814..0f4f9a9b78b 100644 --- a/mps/example/scheme/r4rs.scm +++ b/mps/example/scheme/r4rs.scm @@ -1,4 +1,5 @@ ;;; r4rs.scm -- essential procedures from R4RS +;;; $Id$ ;; (caar pair) ;; (cadr pair) diff --git a/mps/example/scheme/scheme-advanced.c b/mps/example/scheme/scheme-advanced.c index 707390b2962..49672f6a0a3 100644 --- a/mps/example/scheme/scheme-advanced.c +++ b/mps/example/scheme/scheme-advanced.c @@ -1,6 +1,6 @@ /* scheme.c -- SCHEME INTERPRETER EXAMPLE FOR THE MEMORY POOL SYSTEM * - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. * * This is a toy interpreter for a subset of the Scheme programming * language . @@ -4330,8 +4330,8 @@ static int start(int argc, char *argv[]) topic/root. */ symtab = NULL; res = mps_root_create_area(&symtab_root, arena, mps_rank_exact(), 0, - &symtab, &symtab + 1, - mps_scan_area, NULL); + &symtab, &symtab + 1, + mps_scan_area, NULL); if(res != MPS_RES_OK) error("Couldn't register symtab root"); /* The symbol table is strong-key weak-value. */ @@ -4619,7 +4619,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/example/scheme/scheme.c b/mps/example/scheme/scheme.c index 4a10b5c9b10..1ba280c7d59 100644 --- a/mps/example/scheme/scheme.c +++ b/mps/example/scheme/scheme.c @@ -1,6 +1,6 @@ /* scheme.c -- SCHEME INTERPRETER EXAMPLE FOR THE MEMORY POOL SYSTEM * - * Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. + * Copyright (c) 2001-2016 Ravenbrook Limited. See end of file for license. * * This is a toy interpreter for a subset of the Scheme programming * language . @@ -776,8 +776,8 @@ static void rehash(void) { that both copies are updated atomically to the mutator (this interpreter). */ res = mps_root_create_area(&symtab_root, arena, mps_rank_exact(), 0, - symtab, symtab + symtab_size, - mps_scan_area, NULL); + symtab, symtab + symtab_size, + mps_scan_area, NULL); if(res != MPS_RES_OK) error("Couldn't register new symtab root"); for(i = 0; i < old_symtab_size; ++i) @@ -4262,7 +4262,7 @@ static int start(int argc, char *argv[]) assertion failures). See topic/root. */ res = mps_root_create_area(&symtab_root, arena, mps_rank_exact(), 0, symtab, symtab + symtab_size, - mps_scan_area, NULL); + mps_scan_area, NULL); if(res != MPS_RES_OK) error("Couldn't register symtab root"); error_handler = &jb; @@ -4507,7 +4507,7 @@ int main(int argc, char *argv[]) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2001-2014 Ravenbrook Limited . + * Copyright (C) 2001-2016 Ravenbrook Limited . * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/example/scheme/test-common.scm b/mps/example/scheme/test-common.scm index 3c8ec44feb9..b31d31abdea 100644 --- a/mps/example/scheme/test-common.scm +++ b/mps/example/scheme/test-common.scm @@ -1,4 +1,5 @@ ;;; test-common.scm -- common definitions for the Scheme tests +;;; $Id$ (load "r4rs.scm") diff --git a/mps/example/scheme/test-leaf.scm b/mps/example/scheme/test-leaf.scm index 946d889ad87..d62ac00eb1e 100644 --- a/mps/example/scheme/test-leaf.scm +++ b/mps/example/scheme/test-leaf.scm @@ -1,5 +1,5 @@ ;;; test-leaf.scm -- test leaf objects -;;; +;;; $Id$ ;;; This test case creates many leaf objects (strings and integers). (load "test-common.scm") diff --git a/mps/example/scheme/test-mps.scm b/mps/example/scheme/test-mps.scm index 37f2c2aad92..3cc905c39e8 100644 --- a/mps/example/scheme/test-mps.scm +++ b/mps/example/scheme/test-mps.scm @@ -1,4 +1,5 @@ ;;; test-mps.scm -- tests for the MPS toy Scheme interpreter +;;; $Id$ (load "test-common.scm") diff --git a/mps/example/scheme/test-r5rs.scm b/mps/example/scheme/test-r5rs.scm index 1a1fe853fed..940ab2cdc53 100644 --- a/mps/example/scheme/test-r5rs.scm +++ b/mps/example/scheme/test-r5rs.scm @@ -1,4 +1,5 @@ ;;; test-r5rs.scm -- tests from R5RS +;;; $Id$ ;;; ;;; This file contains test code derived directly from R5RS. It ;;; ensures that all the functions correctly evaluate the examples in diff --git a/mps/example/scheme/test-weak.scm b/mps/example/scheme/test-weak.scm index 5f06bb81040..c623026273a 100644 --- a/mps/example/scheme/test-weak.scm +++ b/mps/example/scheme/test-weak.scm @@ -1,4 +1,5 @@ ;;; test-weak.scm -- weak hashtable tests for the MPS toy Scheme interpreter +;;; $Id$ (load "test-common.scm") diff --git a/mps/manual/.p4ignore b/mps/manual/.p4ignore index 86aa08494ce..f2a531e43c7 100644 --- a/mps/manual/.p4ignore +++ b/mps/manual/.p4ignore @@ -2,4 +2,7 @@ doctrees converted epub html -mmref \ No newline at end of file +mmref +source/design/*.rst +source/design/*.svg +tool diff --git a/mps/manual/.renamed-gitignore b/mps/manual/.renamed-gitignore new file mode 120000 index 00000000000..c5c99a6a89c --- /dev/null +++ b/mps/manual/.renamed-gitignore @@ -0,0 +1 @@ +.p4ignore \ No newline at end of file diff --git a/mps/manual/Makefile b/mps/manual/Makefile index 2b698c1d375..7afddf4994d 100644 --- a/mps/manual/Makefile +++ b/mps/manual/Makefile @@ -1,5 +1,5 @@ # Makefile for Sphinx documentation -# +# $Id$ # You can set these variables from the command line. SPHINXOPTS = diff --git a/mps/manual/build.txt b/mps/manual/build.txt index 7b8e17c51b9..357fb14a49e 100644 --- a/mps/manual/build.txt +++ b/mps/manual/build.txt @@ -34,14 +34,14 @@ Compiling for production In the simplest case, you can compile the MPS to an object file with just:: - cc -c mps.c (Unix/Mac OS X) + cc -c mps.c (Unix/macOS) cl /c mps.c (Windows) This will build a "hot" variety (for production) object file for use with ``mps.h``. You can greatly improve performance by allowing global optimization, for example:: - cc -O2 -c mps.c (Unix/Mac OS X) + cc -O2 -c mps.c (Unix/macOS) cl /O2 /c mps.c (Windows) @@ -51,7 +51,7 @@ Compiling for debugging You can get a "cool" variety MPS (with more internal checking, for debugging and development) with:: - cc -g -DCONFIG_VAR_COOL -c mps.c (Unix/Mac OS X) + cc -g -DCONFIG_VAR_COOL -c mps.c (Unix/macOS) cl /Zi /DCONFIG_VAR_COOL /c mps.c (Windows) @@ -68,7 +68,7 @@ between it and the MPS. So if your format implementation is in, say, then:: - cc -O2 -c mymps.c (Unix/Mac OS X) + cc -O2 -c mymps.c (Unix/macOS) cl /O2 /c mymps.c (Windows) This will get your format code inlined with the MPS garbage collector. @@ -111,7 +111,7 @@ with ``pkg_add -r gmake``. On Windows platforms the NMAKE tool is used. This comes with Microsoft Visual Studio C++ or the Microsoft Windows SDK. -On Mac OS X the MPS is built using Xcode, either by opening +On macOS the MPS is built using Xcode, either by opening ``mps.xcodeproj`` with the Xcode app, or using the command-line "xcodebuild" tool, installed from Xcode → Preferences → Downloads → Components → Command Line Tools. @@ -136,15 +136,16 @@ well: Platform OS Architecture Compiler Makefile ========== ========= ============= ============ ================= ``fri3gc`` FreeBSD IA-32 GCC ``fri3gc.gmk`` -``fri6gc`` FreeBSD x86_64 GCC ``fri6gc.gmk`` +``fri3ll`` FreeBSD IA-32 Clang ``fri3ll.gmk`` +``fri6gc`` FreeBSD x86-64 GCC ``fri6gc.gmk`` +``fri6ll`` FreeBSD x86-64 Clang ``fri6ll.gmk`` ``lii3gc`` Linux IA-32 GCC ``lii3gc.gmk`` -``lii6gc`` Linux x86_64 GCC ``lii6gc.gmk`` -``lii6ll`` Linux x86_64 Clang ``lii6ll.gmk`` -``xci3ll`` Mac OS X IA-32 Clang ``mps.xcodeproj`` -``xci6ll`` Mac OS X x86_64 Clang ``mps.xcodeproj`` -``xci3gc`` Mac OS X IA-32 GCC (legacy) ``xci3gc.gmk`` +``lii6gc`` Linux x86-64 GCC ``lii6gc.gmk`` +``lii6ll`` Linux x86-64 Clang ``lii6ll.gmk`` ``w3i3mv`` Windows IA-32 Microsoft C ``w3i3mv.nmk`` -``w3i6mv`` Windows x86_64 Microsoft C ``w3i6mv.nmk`` +``w3i6mv`` Windows x86-64 Microsoft C ``w3i6mv.nmk`` +``xci3ll`` macOS IA-32 Clang ``mps.xcodeproj`` +``xci6ll`` macOS x86-64 Clang ``mps.xcodeproj`` ========== ========= ============= ============ ================= Historically, the MPS worked on a much wider variety of platforms, and @@ -175,9 +176,9 @@ To build a restricted set of targets for just one variety, run:: make -f 'VARIETY=' For example, to build just the "cool" variety of the ``amcss`` test on -FreeBSD:: +64-bit Linux with Clang:: - gmake -f fri3gc.gmk VARIETY=cool amcss + gmake -f lii6ll.gmk VARIETY=cool amcss On Windows platforms you need to run the "Visual Studio Command Prompt" from the Start menu. Then run one of these commands:: @@ -194,13 +195,13 @@ To build just one target, run one of these commands:: nmake /f w3i3mv.nmk (32-bit) nmake /f w3i6mv.nmk (64-bit) -On Mac OS X, you can build from the command line with:: +On macOS, you can build from the command line with:: xcodebuild On most platforms, the output of the build goes to a directory named -after the platform (e.g. ``fri3gc``) so that you can share the source -tree across platforms. On Mac OS X the output goes in a directory +after the platform (e.g. ``lii6ll``) so that you can share the source +tree across platforms. On macOS the output goes in a directory called ``xc``. Building generates ``mps.a`` or ``mps.lib`` or equivalent, a library of object code which you can link with your application, subject to the :ref:`MPS licensing conditions `. @@ -240,7 +241,7 @@ loads a diagnostic stream of events into a `SQLite3 `_ database for processing. In order to build this program, you need to install the SQLite3 development resources. -* On Mac OS X, SQLite3 is pre-installed, so this tool builds by +* On macOS, SQLite3 is pre-installed, so this tool builds by default. * On Linux, you need to install the ``libsqlite3-dev`` package:: diff --git a/mps/manual/source/code-index.rst b/mps/manual/source/code-index.rst index c38dff4a9cf..170227b3e73 100644 --- a/mps/manual/source/code-index.rst +++ b/mps/manual/source/code-index.rst @@ -23,7 +23,6 @@ mpscams.h :ref:`pool-ams` pool class external interface. mpscawl.h :ref:`pool-awl` pool class external interface. mpsclo.h :ref:`pool-lo` pool class external interface. mpscmfs.h :ref:`pool-mfs` pool class external interface. -mpscmv.h :ref:`pool-mv` pool class external interface. mpscmv2.h Former (deprecated) :ref:`pool-mvt` pool class interface. mpscmvff.h :ref:`pool-mvff` pool class external interface. mpscmvt.h :ref:`pool-mvt` pool class external interface. @@ -124,8 +123,9 @@ protocol.c Inheritance protocol implementation. See design.mps.protocol_. protocol.h Inheritance protocol interface. See design.mps.protocol_. range.c Address ranges implementation. See design.mps.range_. range.h Address ranges interface. See design.mps.range_. +rangetree.c Binary address-ordered range tree implementation. +rangetree.h Binary address-ordered range tree interface. ref.c Ranks and zones implementation. -reserv.c Reservoir pool implementation. See design.mps.reservoir_. ring.c Ring implementation. See design.mps.ring_. ring.h Ring interface. See design.mps.ring_. root.c :ref:`topic-root` implementation. @@ -163,55 +163,48 @@ File Description lock.h Lock interface. See design.mps.lock_. lockan.c Lock implementation for standard C. lockix.c Lock implementation for POSIX. -lockli.c Lock implementation for Linux. lockw3.c Lock implementation for Windows. -prmcan.c Mutator context implementation for standard C. +prmc.h Mutator context interface. See design.mps.prmc_. +prmcan.c Mutator context implementation for generic operating system. +prmcanan.c Mutator context implementation for generic architecture. +prmcfri3.c Mutator context implementation for FreeBSD, IA-32. +prmcfri6.c Mutator context implementation for FreeBSD, x86-64. +prmci3.c Mutator context implementation for IA-32. prmci3.h Mutator context interface for IA-32. -prmci3fr.c Mutator context implementation for FreeBSD, IA-32. -prmci3li.c Mutator context implementation for Linux, IA-32. -prmci3w3.c Mutator context implementation for Windows, IA-32. -prmci3xc.c Mutator context implementation for OS X, IA-32. +prmci6.c Mutator context implementation for x86-64. prmci6.h Mutator context interface for x86-64. -prmci6fr.c Mutator context implementation for FreeBSD, x86-64. -prmci6li.c Mutator context implementation for Linux, x86-64. -prmci6w3.c Mutator context implementation for Windows, x86-64. -prmci6xc.c Mutator context implementation for OS X, x86-64. +prmcix.c Mutator context implementation for POSIX. prmcix.h Mutator context interface for POSIX. +prmclii3.c Mutator context implementation for Linux, IA-32. +prmclii6.c Mutator context implementation for Linux, x86-64. +prmcw3.c Mutator context implementation for Windows. prmcw3.h Mutator context interface for Windows. -prmcxc.h Mutator context interface for OS X. +prmcw3i3.c Mutator context implementation for Windows, IA-32. +prmcw3i6.c Mutator context implementation for Windows, x86-64. +prmcxc.c Mutator context implementation for macOS. +prmcxc.h Mutator context interface for macOS. +prmcxci3.c Mutator context implementation for macOS, IA-32. +prmcxci6.c Mutator context implementation for macOS, x86-64. prot.h Protection interface. See design.mps.prot_. protan.c Protection implementation for standard C. -proti3.c Protection implementation for IA-32. -proti6.c Protection implementation for x86-64. protix.c Protection implementation for POSIX. -protli.c Protection implementation for Linux. protsgix.c Protection implementation for POSIX (signals part). protw3.c Protection implementation for Windows. -protxc.c Protection implementation for OS X. -protxc.h Protection interface for OS X. +protxc.c Protection implementation for macOS. +protxc.h Protection interface for macOS. pthrdext.c Protection implementation for POSIX (threads part). pthrdext.h Protection interface for POSIX (threads part). sp.h Stack probe interface. See design.mps.sp_. span.c Stack probe implementation for standard C. spw3i3.c Stack probe implementation for Windows, IA-32. spw3i6.c Stack probe implementation for Windows, x86-64. -ss.c Stack scanning implementation (common part). -ss.h Stack scanning interface. See design.mps.ss_. -ssan.c Stack scanning implementation for standard C. -ssixi3.c Stack scanning implementation for POSIX, IA-32. -ssixi6.c Stack scanning implementation for POSIX, x86-64. -ssw3i3mv.c Stack scanning implementation for Windows, IA-32, Visual C. -ssw3i3pc.c Stack scanning implementation for Windows, x86-64, Pelles C. -ssw3i6mv.c Stack scanning implementation for Windows, IA-32, Visual C. -ssw3i6pc.c Stack scanning implementation for Windows, x86-64, Pelles C. +ss.c Stack scanning implementation. +ss.h Stack scanning interface. See design.mps.stack-scan_. th.h Threads interface. See design.mps.thread-manager_. than.c Threads implementation for standard C. thix.c Threads implementation for POSIX. thw3.c Threads implementation for Windows. -thw3.h Threads interface for Windows. -thw3i3.c Threads implementation for Windows, IA-32. -thw3i6.c Threads implementation for Windows, x86-64. -thxc.c Threads implementation for OS X. +thxc.c Threads implementation for macOS. vm.c Virtual memory implementation (common part). vm.h Virtual memory interface. See design.mps.vm_. vman.c Virtual memory implementation for standard C. @@ -224,7 +217,7 @@ Pool classes ------------ These files implement the supported :term:`pool classes`. Some of -these (MFS, MV) are used internally by the MPS; the others are +these (MFS, MVFF) are used internally by the MPS; the others are available for :term:`client programs` only. See :ref:`pool`. =========== ================================================================== @@ -237,11 +230,10 @@ poolawl.c :ref:`pool-awl` implementation. poollo.c :ref:`pool-lo` implementation. poolmfs.c :ref:`pool-mfs` implementation. poolmfs.h :ref:`pool-mfs` internal interface. -poolmv.c :ref:`pool-mv` implementation. -poolmv.h :ref:`pool-mv` internal interface. poolmv2.c :ref:`pool-amc` implementation. poolmv2.h :ref:`pool-mvt` internal interface. poolmvff.c :ref:`pool-mvff` implementation. +poolmvff.h :ref:`pool-mvff` internal interface. poolsnc.c :ref:`pool-snc` implementation. =========== ================================================================== @@ -256,13 +248,10 @@ These files implement auxiliary programs. See File Description =========== ================================================================== eventcnv.c :ref:`telemetry-mpseventcnv`. -eventrep.c Event replaying implementation (broken). -eventrep.h Event replaying interface (broken). eventsql.c :ref:`telemetry-mpseventsql`. eventtxt.c :ref:`telemetry-mpseventtxt`. getopt.h Command-line option interface. Adapted from FreeBSD. getoptl.c Command-line option implementation. Adapted from FreeBSD. -replay.c Event replaying program (broken). table.c Address-based hash table implementation. table.h Address-based hash table interface. =========== ================================================================== @@ -347,9 +336,9 @@ awlutth.c :ref:`pool-awl` unit test (using multiple threads). btcv.c Bit table coverage test. exposet0.c :c:func:`mps_arena_expose` test. expt825.c Regression test for job000825_. -fbmtest.c Free block manager (CBS and Freelist) test. finalcv.c :ref:`topic-finalization` coverage test. finaltest.c :ref:`topic-finalization` test. +forktest.c :ref:`topic-thread-fork` test. fotest.c Failover allocator test. landtest.c Land test. locbwcss.c Locus backwards compatibility stress test. @@ -368,7 +357,7 @@ sacss.c :ref:`topic-cache` stress test. segsmss.c Segment splitting and merging stress test. steptest.c :c:func:`mps_arena_step` test. tagtest.c Tagged pointer scanning test. -walkt0.c Formatted object walking test. +walkt0.c Roots and formatted objects walking test. zcoll.c Garbage collection progress test. zmess.c Garbage collection and finalization message test. ================ ============================================================= @@ -406,6 +395,8 @@ w3i3pc.nmk NMAKE file for platform W3I3PC. w3i6mv.nmk NMAKE file for platform W3I6MV. w3i6pc.nmk NMAKE file for platform W3I6PC. xci3gc.gmk GNU makefile for platform XCI3GC. +xci3ll.gmk GNU makefile for platform XCI3LL. +xci6gc.gmk GNU makefile for platform XCI6GC. xci6ll.gmk GNU makefile for platform XCI6LL. ============= ================================================================ @@ -431,13 +422,12 @@ xci6ll.gmk GNU makefile for platform XCI6LL. .. _design.mps.protocol: design/protocol.html .. _design.mps.prot: design/prot.html .. _design.mps.range: design/range.html -.. _design.mps.reservoir: design/reservoir.html .. _design.mps.ring: design/ring.html .. _design.mps.seg: design/seg.html .. _design.mps.shield: design/shield.html .. _design.mps.sp: design/sp.html .. _design.mps.splay: design/splay.html -.. _design.mps.ss: design/ss.html +.. _design.mps.stack-scan: design/stack-scan.html .. _design.mps.strategy: design/strategy.html .. _design.mps.tests: design/tests.html .. _design.mps.testthr: design/testthr.html diff --git a/mps/manual/source/conf.py b/mps/manual/source/conf.py index 733438fc253..28c757e0ac5 100644 --- a/mps/manual/source/conf.py +++ b/mps/manual/source/conf.py @@ -144,10 +144,6 @@ # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -html_use_smartypants = True - # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} diff --git a/mps/manual/source/design/.renamed-gitignore b/mps/manual/source/design/.renamed-gitignore new file mode 120000 index 00000000000..c5c99a6a89c --- /dev/null +++ b/mps/manual/source/design/.renamed-gitignore @@ -0,0 +1 @@ +.p4ignore \ No newline at end of file diff --git a/mps/manual/source/design/index.rst b/mps/manual/source/design/index.rst index 1cea6c4861b..5e75b6ec6d7 100644 --- a/mps/manual/source/design/index.rst +++ b/mps/manual/source/design/index.rst @@ -15,6 +15,7 @@ Design critical-path exec-env failover + finalize freelist guide.hex.trans guide.impl.c.format @@ -24,17 +25,24 @@ Design keyword-arguments land lock + message nailboard + pool prmc prot + protix range ring - sp + shield sig + sp splay - ss + stack-scan testthr thread-manager + thread-safety type + version-library vm + write-barrier writef diff --git a/mps/manual/source/design/old.rst b/mps/manual/source/design/old.rst index 8a8e71be1a6..752c3ea97a0 100644 --- a/mps/manual/source/design/old.rst +++ b/mps/manual/source/design/old.rst @@ -20,43 +20,28 @@ Old design bt buffer check - class-interface collection diag - finalize fix io lib locus - message message-gc object-debug - pool poolamc poolams poolawl poollo poolmfs poolmrg - poolmv poolmvt poolmvff - protli - protsu protocol pthreadext - reservoir root scan seg - shield - sso1al strategy telemetry tests - thread-safety trace - version-library - version - vmo1 - vmso diff --git a/mps/manual/source/extensions/mps/__init__.py b/mps/manual/source/extensions/mps/__init__.py index b5797dc0796..466aaef3359 100644 --- a/mps/manual/source/extensions/mps/__init__.py +++ b/mps/manual/source/extensions/mps/__init__.py @@ -6,17 +6,40 @@ from collections import defaultdict from inspect import isabstract, isclass import re -from . import designs +import warnings from docutils import nodes, transforms +from docutils.parsers.rst import Directive +from docutils.parsers.rst.directives.admonitions import BaseAdmonition from sphinx import addnodes from sphinx.directives.other import VersionChange from sphinx.domains import Domain from sphinx.roles import XRefRole -from sphinx.util.compat import Directive, make_admonition from sphinx.util.nodes import set_source_info, process_index_entry -from sphinx.locale import versionlabels -versionlabels['deprecatedstarting'] = 'Deprecated starting with version %s' +from sphinx.locale import admonitionlabels, versionlabels + +from . import designs + +versionlabels['deprecatedstarting'] = "Deprecated starting with version %s" +admonitionlabels.update( + aka="Also known as", + bibref="Related publication", + bibrefs="Related publications", + deprecated="Deprecated", + historical="Historical note", + link="Related link", + links="Related links", + note="Note", + notes="Notes", + opposite="Opposite term", + opposites="Opposite terms", + relevance="Relevance to memory management", + see="See", + similar="Similar term", + similars="Similar terms", + specific="In the MPS", + topics="Topic", + topicss="Topics"), class MpsDomain(Domain): label = 'MPS' @@ -25,12 +48,15 @@ class MpsDomain(Domain): class MpsDirective(Directive): @classmethod def add_to_app(cls, app): - if hasattr(cls, 'name'): name = cls.name - elif hasattr(cls, 'nodecls'): name = cls.nodecls.__name__ - else: return - if hasattr(cls, 'nodecls') and hasattr(cls, 'visit'): - app.add_node(cls.nodecls, html = cls.visit, latex = cls.visit, - text = cls.visit, man = cls.visit) + if hasattr(cls, 'name'): + name = cls.name + elif hasattr(cls, 'node_class') and cls.node_class is not None: + name = cls.node_class.__name__ + else: + return + if hasattr(cls, 'node_class') and hasattr(cls, 'visit'): + app.add_node(cls.node_class, html=cls.visit, latex=cls.visit, + text=cls.visit, man=cls.visit) if hasattr(cls, 'domain'): app.add_directive_to_domain(cls.domain, name, cls) else: @@ -82,143 +108,110 @@ def mps_ref_role(name, rawtext, text, lineno, inliner, options={}, content=[]): return [refnode], [] class Admonition(nodes.Admonition, nodes.Element): - pass + plural = False def visit_admonition_node(self, node): - self.visit_admonition(node) + name = type(node).__name__ + ('s' if node.plural else '') + self.visit_admonition(node, name=name) def depart_admonition_node(self, node): self.depart_admonition(node) -class AdmonitionDirective(MpsDirective): - label = 'Admonition' +class AdmonitionDirective(MpsDirective, BaseAdmonition): has_content = True visit = visit_admonition_node, depart_admonition_node - @classmethod - def add_to_app(cls, app): - if not hasattr(cls, 'nodecls'): return - super(AdmonitionDirective, cls).add_to_app(app) - - def run(self): - ad = make_admonition(self.nodecls, self.name, [self.label], - self.options, self.content, self.lineno, - self.content_offset, self.block_text, - self.state, self.state_machine) - return ad - class PluralDirective(AdmonitionDirective): def run(self): ad = super(PluralDirective, self).run() - refs = sum(1 for node in ad[0][1] - if isinstance(node, addnodes.pending_xref) - or isinstance(node, nodes.Referential)) + refs = sum(1 for node in ad[0][0] + if isinstance(node, (addnodes.pending_xref, + nodes.Referential))) if refs > 1: - assert(isinstance(ad[0][0], nodes.title)) - ad[0][0][0] = nodes.Text(self.plural) + ad[0].plural = True return ad class aka(Admonition): pass class AkaDirective(AdmonitionDirective): - nodecls = aka - label = 'Also known as' + node_class = aka class bibref(Admonition): pass class BibrefDirective(PluralDirective): - nodecls = bibref - label = 'Related publication' - plural = 'Related publications' + node_class = bibref class deprecated(Admonition): pass class DeprecatedDirective(AdmonitionDirective): - nodecls = deprecated - label = 'Deprecated' + node_class = deprecated class historical(Admonition): pass class HistoricalDirective(AdmonitionDirective): - nodecls = historical - label = 'Historical note' + node_class = historical class link(Admonition): pass class LinkDirective(PluralDirective): - nodecls = link - label = 'Related link' - plural = 'Related links' + node_class = link class note(Admonition): pass class NoteDirective(AdmonitionDirective): - nodecls = note - label = 'Note' - plural = 'Notes' + node_class = note def run(self): ad = super(NoteDirective, self).run() - assert(isinstance(ad[0][0], nodes.title)) - if len(ad[0]) == 1: return ad - if (isinstance(ad[0][1], nodes.enumerated_list) - and sum(1 for _ in ad[0][1].traverse(nodes.list_item)) > 1 - or isinstance(ad[0][1], nodes.footnote) + if (isinstance(ad[0][0], nodes.enumerated_list) + and sum(1 for _ in ad[0][0].traverse(nodes.list_item)) > 1 + or isinstance(ad[0][0], nodes.footnote) and sum(1 for _ in ad[0].traverse(nodes.footnote)) > 1): - ad[0][0][0] = nodes.Text(self.plural) + ad[0].plural = True return ad class opposite(Admonition): pass class OppositeDirective(PluralDirective): - nodecls = opposite - label = 'Opposite term' - plural = 'Opposite terms' + node_class = opposite class relevance(Admonition): pass class RelevanceDirective(AdmonitionDirective): - nodecls = relevance - label = 'Relevance to memory management' + node_class = relevance class see(Admonition): pass class SeeDirective(AdmonitionDirective): - nodecls = see - label = 'See' + node_class = see class similar(Admonition): pass class SimilarDirective(PluralDirective): - nodecls = similar - label = 'Similar term' - plural = 'Similar terms' + node_class = similar class specific(Admonition): pass class SpecificDirective(AdmonitionDirective): domain = 'mps' - nodecls = specific - label = 'In the MPS' + node_class = specific class topics(Admonition): pass class TopicsDirective(PluralDirective): - nodecls = topics - label = 'Topic' - plural = 'Topics' + node_class = topics class GlossaryTransform(transforms.Transform): """ @@ -329,8 +322,6 @@ def warn_indirect_terms(cls, app, exception): print('{}:{}: WARNING: cross-reference to {}.' .format(doc, line, i)) - - def setup(app): designs.convert_updated(app) app.add_domain(MpsDomain) diff --git a/mps/manual/source/extensions/mps/designs.py b/mps/manual/source/extensions/mps/designs.py index ff991612fc5..faa2a3ecb6f 100644 --- a/mps/manual/source/extensions/mps/designs.py +++ b/mps/manual/source/extensions/mps/designs.py @@ -20,19 +20,19 @@ AccessSet Accumulation Addr Align AllocFrame AllocPattern AP Arg Arena Attr Bool BootBlock BT Buffer BufferMode Byte Chain Chunk - Clock Compare Count Epoch EventClock 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 + Clock Compare Count Epoch EventClock FindDelete Format Fun GenDesc + Globals Index Land LD Lock LocusPref LocusPrefKind Message + MessageType MutatorContext MutatorContextVar Page Pointer Pool + PoolGen PThreadext Range Rank RankSet ReadonlyAddr Ref RefSet Res + 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 ''' mode = re.compile(r'\.\. mode: .*\n') prefix = re.compile(r'^:Tag: ([a-z][a-z.0-9-]*[a-z0-9])$', re.MULTILINE) -rst_tag = re.compile(r'^:(?:Author|Date|Status|Revision|Copyright|Organization|Format|Index terms):.*?$\n', re.MULTILINE | re.IGNORECASE) +rst_tag = re.compile(r'^:(?:Author|Date|Status|Revision|Copyright|Organization|Format|Index terms|Readership):.*?$\n', re.MULTILINE | re.IGNORECASE) mps_tag = re.compile(r'_`\.([a-z][A-Za-z.0-9_-]*[A-Za-z0-9])`:') mps_ref = re.compile(r'`(\.[a-z][A-Za-z.0-9_-]*[A-Za-z0-9])`_(?: )?') funcdef = re.compile(r'^``([^`]*\([^`]*\))``$', re.MULTILINE) @@ -43,7 +43,7 @@ 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) +design_frag_ref = re.compile(r'^( *\.\. _design\.mps\.([^:\n]+)\.([^:\n]+): (?:[^#:\n]+))#(.+)$', re.MULTILINE) history = re.compile(r'^Document History\n.*', re.MULTILINE | re.IGNORECASE | re.DOTALL) @@ -61,10 +61,10 @@ def secnum_sub(m): # .. [THVV_1995] Tom Van Vleck. 1995. "`Structure Marking `__". citation = re.compile( r''' - ^\.\.\s+(?P\[.*?\])\s* - "(?P[^"]*?)"\s* - ;\s*(?P<author>[^;]*?)\s* - (?:;\s*(?P<organization>[^;]*?)\s*)? + ^\.\.\s+(?P<ref>\[[^\n\]]+\])\s* + "(?P<title>[^"]+?)"\s* + ;\s*(?P<author>[^;]+?)\s* + (?:;\s*(?P<organization>[^;]+?)\s*)? ;\s*(?P<date>[0-9-]+)\s* (?:;\s*<\s*(?P<url>[^>]*?)\s*>\s*)? \. @@ -72,21 +72,19 @@ def secnum_sub(m): re.VERBOSE | re.MULTILINE | re.IGNORECASE | re.DOTALL ) def citation_sub(m): - groups = m.groupdict() - for key in groups: - if groups[key]: - groups[key] = re.sub(r'\s+', ' ', groups[key]) - result = '.. {ref} {author}.'.format(**groups) - if groups.get('organization'): - result += ' {organization}.'.format(**groups) - result += ' {date}.'.format(**groups) - if groups.get('url'): - result += ' "`{title} <{url}>`__".'.format(**groups) + groups = {k: re.sub(r'\s+', ' ', v) for k, v in m.groupdict().items() if v} + fmt = '.. {ref} {author}.' + if 'organization' in groups: + fmt += ' {organization}.' + fmt += ' {date}.' + if 'url' in groups: + fmt += ' "`{title} <{url}>`__".' else: - result += ' "{title}".'.format(**groups) - return result + fmt += ' "{title}".' + return fmt.format(**groups) -index = re.compile(r'^:Index\s+terms:(.*$\n(?:[ \t]+.*$\n)*)', re.MULTILINE | re.IGNORECASE) +index = re.compile(r'^:Index\s+terms:(.*$\n(?:[ \t]+.*$\n)*)', + re.MULTILINE | re.IGNORECASE) # <http://sphinx-doc.org/markup/misc.html#directive-index> index_term = re.compile(r'^\s*(\w+):\s*(.*?)\s*$', re.MULTILINE) diff --git a/mps/manual/source/glossary/b.rst b/mps/manual/source/glossary/b.rst index 12d4838e466..a323a1b8ef4 100644 --- a/mps/manual/source/glossary/b.rst +++ b/mps/manual/source/glossary/b.rst @@ -297,15 +297,16 @@ Memory Management Glossary: B ``brk`` is a Unix system call that sets the limit of the data segment. This limit is known as the *break*. - The :term:`C` library implementation of :term:`malloc` usually - :term:`allocates` :term:`memory (2)` for the - :term:`heap` by extending the data segment using ``brk`` or - :term:`sbrk`. + ``brk`` and its companion :term:`sbrk` are obsolete on Unix + systems that support :term:`virtual memory` and the ``mmap`` + system call. - Most implementations of ``malloc`` never shrink the data - segment, so the memory usage of a process never decreases. In - most Unix systems, the data segment resides immediately above - the program code (text segment) in the :term:`address space`. + The :term:`C` library implementation of :term:`malloc` + formerly :term:`allocated` :term:`memory (2)` for the + :term:`heap` by extending the data segment using ``brk`` or + ``sbrk``. The data segment resided immediately above the + program code and :term:`static data <static allocation>` (the + "text segment") in the :term:`address space`. .. figure:: ../diagrams/brk.svg :align: center @@ -313,6 +314,11 @@ Memory Management Glossary: B A simplified view of the address space of a Unix process. + More modern Unix systems use :term:`address space layout + randomization` to place these segments at randomized locations + in :term:`address space`, so that the :term:`heap` is no + longer adjacent to the static data. + broken heart :term:`Copying garbage collectors <copying garbage diff --git a/mps/manual/source/glossary/c.rst b/mps/manual/source/glossary/c.rst index 2165bac2394..478276e619b 100644 --- a/mps/manual/source/glossary/c.rst +++ b/mps/manual/source/glossary/c.rst @@ -193,14 +193,14 @@ Memory Management Glossary: C .. mps:specific:: - One of the three states an :term:`arena` can be in (the - others being the :term:`unclamped state` and the - :term:`parked state`). In the clamped state, no object - motion occurs and the staleness of :term:`location - dependencies` does not change. However, a :term:`garbage - collection` may be in progress. Call - :c:func:`mps_arena_clamp` to put an arena into the clamped - state. + One of the four states an :term:`arena` can be in (the + others being the :term:`unclamped state`, the + :term:`parked state`, and the :term:`postmortem state`). + In the clamped state, no object motion occurs and the + staleness of :term:`location dependencies` does not + change. However, a :term:`garbage collection` may be in + progress. Call :c:func:`mps_arena_clamp` to put an arena + into the clamped state. client arena diff --git a/mps/manual/source/glossary/d.rst b/mps/manual/source/glossary/d.rst index 80d41072bbc..95abb12cfd3 100644 --- a/mps/manual/source/glossary/d.rst +++ b/mps/manual/source/glossary/d.rst @@ -144,16 +144,6 @@ Memory Management Glossary: D .. see:: :term:`interior pointer`. - derived type - - .. mps:specific:: - - In the MPS interface, a *derived type* is a type that is - neither an :term:`opaque type` nor a :term:`transparent - type`, but is instead a structure or function type based - on transparent and opaque types and on built-in C types. - See :ref:`topic-interface`. - destructor (1) A destructor is a function or a method that performs the diff --git a/mps/manual/source/glossary/index.rst b/mps/manual/source/glossary/index.rst index 6c15cbd4e84..e1fdb3af054 100644 --- a/mps/manual/source/glossary/index.rst +++ b/mps/manual/source/glossary/index.rst @@ -127,6 +127,7 @@ All :term:`client program <mutator>` :term:`closure` :term:`coalesce` +:term:`cold end` :term:`collect` :term:`collection <collection cycle>` :term:`collection cycle` @@ -169,7 +170,6 @@ All :term:`deferred reference counting` :term:`dependent object` :term:`derived pointer <interior pointer>` -:term:`derived type` :term:`destructor (1)` :term:`destructor (2)` :term:`DGC <distributed garbage collection>` @@ -264,6 +264,7 @@ All :term:`hit` :term:`hit rate` :term:`hot` +:term:`hot end` :term:`huge page` :term:`immediate data` @@ -411,6 +412,7 @@ All :term:`pointer` :term:`pool` :term:`pool class` +:term:`postmortem state` :term:`precise garbage collection <exact garbage collection>` :term:`precise reference <exact reference>` :term:`precise root <exact root>` @@ -420,6 +422,7 @@ All :term:`primary storage <main memory>` :term:`promotion` :term:`protectable root` +:term:`protected` :term:`protection` :term:`protection exception <protection fault>` :term:`protection fault` @@ -583,6 +586,7 @@ All :term:`unclamped state` :term:`undead` :term:`unmapped` +:term:`unprotected` :term:`unreachable` :term:`unsure reference <ambiguous reference>` :term:`unwrapped` diff --git a/mps/manual/source/glossary/m.rst b/mps/manual/source/glossary/m.rst index 92abdcf2740..e078367fbd0 100644 --- a/mps/manual/source/glossary/m.rst +++ b/mps/manual/source/glossary/m.rst @@ -30,8 +30,8 @@ Memory Management Glossary: M but faster and more expensive than :term:`backing store`. It is common to refer only to the main memory of a computer; - for example, "This server has 128 GB of memory" and "OS X 10.8 - requires at least 2 GB of memory". + for example, "This server has 128 GB of memory" and "macOS + High Sierra requires at least 2 GB of memory". .. historical:: diff --git a/mps/manual/source/glossary/o.rst b/mps/manual/source/glossary/o.rst index 6a22abfed26..c58f9bf32c6 100644 --- a/mps/manual/source/glossary/o.rst +++ b/mps/manual/source/glossary/o.rst @@ -127,7 +127,7 @@ Memory Management Glossary: O mps_arena_s *``, but the implementation of ``struct mps_arena_s`` is not public. See :ref:`topic-interface`. - .. opposite:: :term:`derived type`, :term:`transparent type`. + .. opposite:: :term:`transparent type`. out parameter diff --git a/mps/manual/source/glossary/p.rst b/mps/manual/source/glossary/p.rst index 4ca1dbcd275..4b21905ea01 100644 --- a/mps/manual/source/glossary/p.rst +++ b/mps/manual/source/glossary/p.rst @@ -179,14 +179,15 @@ Memory Management Glossary: P .. mps:specific:: - One of the three states an :term:`arena` can be in (the - others being the :term:`clamped state` and the - :term:`unclamped state`). In the parked state, no - :term:`garbage collection` is in progress, no object - motion occurs and the staleness of :term:`location - dependencies` does not change. Call - :c:func:`mps_arena_park` or :c:func:`mps_arena_collect` to - put an arena into the parked state. + One of the four states an :term:`arena` can be in (the + others being the :term:`clamped state`, the + :term:`postmortem state`, and the :term:`unclamped + state`). In the parked state, no :term:`garbage + collection` is in progress, no object motion occurs and + the staleness of :term:`location dependencies` does not + change. Call :c:func:`mps_arena_park` or + :c:func:`mps_arena_collect` to put an arena into the + parked state. perfect fit @@ -402,6 +403,21 @@ Memory Management Glossary: P class of :term:`pools` that manage memory according to particular policy. See :ref:`pool`. + postmortem state + + .. mps:specific:: + + One of the four states an :term:`arena` can be in (the + others being the :term:`unclamped state`, the + :term:`clamped state`, and the :term:`parked state`). In + the postmortem state, objects do not move in memory, the + staleness of :term:`location dependencies` does not + change, memory occupied by :term:`unreachable` objects is + not recycled, all memory protection is removed, and memory + may be in an inconsistent state. Call + :c:func:`mps_arena_postmortem` to put an arena into the + postmortem state. + precise garbage collection .. see:: :term:`exact garbage collection`. @@ -489,6 +505,13 @@ Memory Management Glossary: P :c:macro:`MPS_RM_PROT` when calling a registration function such as :c:func:`mps_root_create`. + protected + + A region of :term:`memory (2)` is said to be protected if + there is a :term:`barrier (1)` on that region. + + .. opposite:: :term:`unprotected` + protection .. aka:: *memory protection*, *page protection*. diff --git a/mps/manual/source/glossary/s.rst b/mps/manual/source/glossary/s.rst index 269052b3499..c0649e056bd 100644 --- a/mps/manual/source/glossary/s.rst +++ b/mps/manual/source/glossary/s.rst @@ -13,12 +13,11 @@ Memory Management Glossary: S ``sbrk`` is a Unix library function that adjusts the limit of the data segment; this limit is known as the *break*. - ``sbrk`` returns the previous value of the break, so - ``sbrk(0)`` is a common idiom for getting the current value. + ``sbrk`` and its companion :term:`brk` are obsolete on Unix + systems that support :term:`virtual memory`. - Note that, if you use :term:`brk`, you probably can't safely - use ``sbrk`` as well, because it may store the last value of - the break in a private variable. + ``sbrk`` returns the previous value of the break, so + ``sbrk(0)`` was a common idiom for getting the current value. scalar data type diff --git a/mps/manual/source/glossary/t.rst b/mps/manual/source/glossary/t.rst index 1b4411a8e6c..a66d498438d 100644 --- a/mps/manual/source/glossary/t.rst +++ b/mps/manual/source/glossary/t.rst @@ -264,7 +264,7 @@ Memory Management Glossary: T example, :c:type:`mps_addr_t` is a transparent alias for ``void *``. See :ref:`topic-interface`. - .. opposite:: :term:`derived type`, :term:`opaque type`. + .. opposite:: :term:`opaque type`. transport diff --git a/mps/manual/source/glossary/u.rst b/mps/manual/source/glossary/u.rst index 71f8fe78ac3..de251815313 100644 --- a/mps/manual/source/glossary/u.rst +++ b/mps/manual/source/glossary/u.rst @@ -49,12 +49,12 @@ Memory Management Glossary: U .. mps:specific:: - One of the three states an :term:`arena` can be in (the - others being the :term:`clamped state` and the - :term:`parked state`). In the unclamped state, object - motion and other background activity may occur. Call - :c:func:`mps_arena_release` to put an arena into the - unclamped state. + One of the four states an :term:`arena` can be in (the + others being the :term:`clamped state`, the :term:`parked + state` and the :term:`postmortem state`). In the unclamped + state, object motion and other background activity may + occur. Call :c:func:`mps_arena_release` to put an arena + into the unclamped state. undead @@ -80,6 +80,13 @@ Memory Management Glossary: U .. opposite:: :term:`mapped`. + unprotected + + A region of :term:`memory (2)` is said to be unprotected if + there are no :term:`barriers (1)` on that region. + + .. opposite:: :term:`protected` + unreachable An :term:`object` is unreachable if there is no diff --git a/mps/manual/source/guide/advanced.rst b/mps/manual/source/guide/advanced.rst index b56edf14fa4..c7cc53af363 100644 --- a/mps/manual/source/guide/advanced.rst +++ b/mps/manual/source/guide/advanced.rst @@ -141,9 +141,9 @@ releasing the resource (here, the Scheme function 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. +necessary to make ports robust against being 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: 6 diff --git a/mps/manual/source/guide/debug.rst b/mps/manual/source/guide/debug.rst index 3c1678e660b..76e0bb8f42a 100644 --- a/mps/manual/source/guide/debug.rst +++ b/mps/manual/source/guide/debug.rst @@ -91,9 +91,34 @@ General debugging advice 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 + On macOS, barrier hits do not use signals and so do not enter the debugger. +#. .. index:: + single: postmortem debugging + single: postmortem state + + If the :term:`client program` is stopped in the debugger with the + MPS part of the way through execution of an operation in an + :term:`arena` (for example, a crash inside a :term:`scan method`), + it will not be possible to call introspection functions, such as + :c:func:`mps_arena_has_addr` or :c:func:`mps_addr_pool` (because + the MPS is not re-entrant), and it may not be possible to examine + some regions of memory (because they are :term:`protected` by the + MPS). + + If you are in this situation and would like to be able to call MPS + functions or examine regions of memory from the debugger, then you + can put the arena into the :term:`postmortem state` by calling + :c:func:`mps_arena_postmortem` from the debugger. This unlocks the + arena and turns off protection. + + .. warning:: + + After calling :c:func:`mps_arena_postmortem`, MPS-managed + memory is not in a consistent state, and so it is not safe to + continue running the client program. + .. index:: single: ASLR @@ -130,7 +155,7 @@ program (data segment, text segment, stack and heap): } When ASLR is turned on, running this program outputs different -addresses on each run. For example, here are four runs on OS X +addresses on each run. For example, here are four runs on macOS 10.9.3:: data: 0x10a532020 text: 0x10a531ed0 stack: 0x7fff556ceb1c heap: 0x7f9f80c03980 @@ -171,7 +196,7 @@ Here's the situation on each of the operating systems supported by the MPS: $ setarch $(uname -m) -R ./myprogram -* On **OS X** (10.7 or later), ASLR can be disabled for a single +* On **macOS** (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: @@ -188,7 +213,7 @@ Here's the situation on each of the operating systems supported by the MPS: 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 + works on macOS 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 <contact>`.) diff --git a/mps/manual/source/guide/lang.rst b/mps/manual/source/guide/lang.rst index 6ae1ce60aaf..324a6305563 100644 --- a/mps/manual/source/guide/lang.rst +++ b/mps/manual/source/guide/lang.rst @@ -976,8 +976,8 @@ as a root by calling :c:func:`mps_root_create_thread`:: void *marker = ▮ mps_root_t stack_root; - res = mps_root_create_thread(®_root, arena, thread, marker); - if (res != MPS_RES_OK) error("Couldn't create root"); + res = mps_root_create_thread(&stack_root, arena, thread, marker); + if (res != MPS_RES_OK) error("Couldn't create stack root"); In order to scan the control stack, the MPS needs to know where the :term:`cold end` of the stack is, and that's the role of the @@ -1175,13 +1175,13 @@ 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_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 */ + 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_root_destroy(stack_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 */ What next? diff --git a/mps/manual/source/guide/overview.rst b/mps/manual/source/guide/overview.rst index 3156469f27e..c813aaf34c4 100644 --- a/mps/manual/source/guide/overview.rst +++ b/mps/manual/source/guide/overview.rst @@ -43,19 +43,22 @@ for details. single: Memory Pool System; supported target platforms single: platforms; supported +.. _guide-overview-platforms: + 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 Vista or later, on IA-32 and x86-64, using Microsoft Visual + C/C++; -- Linux 2.4 or later, on IA-32 using GCC and on x86-64 using GCC or +- Linux 2.6 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; +- FreeBSD 7 or later, on IA-32 and x86-64, using GCC or Clang/LLVM; -- OS X 10.4 or later, on IA-32 and x86-64, using Clang/LLVM. +- macOS 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 diff --git a/mps/manual/source/guide/perf.rst b/mps/manual/source/guide/perf.rst index 805d706f169..adea19cc761 100644 --- a/mps/manual/source/guide/perf.rst +++ b/mps/manual/source/guide/perf.rst @@ -34,45 +34,32 @@ short-lived objects.) First, the effect of varying the capacity of a chain with a single generation. -======== ========= ========================= -Capacity Mortality Execution time (user+sys) -======== ========= ========================= -100 0.80 362.6 -200 0.80 354.9 -400 0.80 349.7 -800 0.80 314.4 -1600 0.80 215.7 -3200 0.80 94.0 -6400 0.80 53.5 -12800 0.80 79.6 -25600 0.80 77.6 -======== ========= ========================= +======== ========================= +Capacity Execution time (user+sys) +======== ========================= +100 362.6 +200 354.9 +400 349.7 +800 314.4 +1600 215.7 +3200 94.0 +6400 53.5 +12800 79.6 +25600 77.6 +======== ========================= -Second, the effect of varying the mortality of a chain with a single -generation. - -======== ========= ========================= -Capacity Mortality Execution time (user+sys) -======== ========= ========================= -6400 0.20 55.4 -6400 0.40 54.0 -6400 0.60 54.0 -6400 0.80 53.5 -6400 0.99 54.8 -======== ========= ========================= - -Third, the effect of varying the number of generations (all +Second, the effect of varying the number of generations (all generations being identical). -=========== ======== ========= ========================= -Generations Capacity Mortality Execution time (user+sys) -=========== ======== ========= ========================= -1 6400 0.80 53.5 -2 6400 0.80 42.4 -3 6400 0.80 42.1 -4 6400 0.80 42.2 -5 6400 0.80 42.2 -=========== ======== ========= ========================= +=========== ======== ========================= +Generations Capacity Execution time (user+sys) +=========== ======== ========================= +1 6400 53.5 +2 6400 42.4 +3 6400 42.1 +4 6400 42.2 +5 6400 42.2 +=========== ======== ========================= These tables suggest that: @@ -80,10 +67,6 @@ These tables suggest that: sizes right is dramatic: much bigger than the small improvements to gained from other techniques. -#. The predicted mortality doesn't make much difference to the overall - execution time (it does affect the distribution of pause times, - however: see :ref:`topic-collection-schedule`.) - #. You can make generations too big as well as too small. #. There are rapidly diminishing returns to be gained from adding @@ -97,7 +80,7 @@ These tables suggest that: The table below shows the effect of varying the initial allocation of address space to the arena (using three generations each with capacity -6400 kB, mortality 0.80). +6400 kB). ============= ========== =========== ========================= Address space Extensions Collections Execution time (user+sys) diff --git a/mps/manual/source/mmref/lang.rst b/mps/manual/source/mmref/lang.rst index 7462f89742b..19dab83e7ba 100644 --- a/mps/manual/source/mmref/lang.rst +++ b/mps/manual/source/mmref/lang.rst @@ -539,7 +539,7 @@ Memory management in various languages .. link:: - `Borland Delphi Home Page <http://www.borland.com/delphi/>`_, + `Embarcadero (formely Borland) Delphi <https://www.embarcadero.com/products/delphi>`_, `Pascal standardization <http://www.open-std.org/JTC1/sc22/docs/oldwgs/wg2.html>`_. Perl diff --git a/mps/manual/source/pool/amc.rst b/mps/manual/source/pool/amc.rst index a21594202c6..f768a3a389a 100644 --- a/mps/manual/source/pool/amc.rst +++ b/mps/manual/source/pool/amc.rst @@ -4,7 +4,7 @@ `<https://info.ravenbrook.com/project/mps/master/design/poolamc/>`_ .. index:: - single: AMC + single: AMC pool class single: pool class; AMC .. _pool-amc: @@ -33,7 +33,7 @@ the following tendencies will be efficiently exploited by an AMC pool: .. index:: - single: AMC; properties + single: AMC pool class; properties AMC properties -------------- @@ -92,7 +92,7 @@ AMC properties .. index:: - single: AMC; interface + single: AMC pool class; interface AMC interface ------------- @@ -142,7 +142,7 @@ AMC interface .. index:: - pair: AMC; introspection + pair: AMC pool class; introspection .. _pool-amc-introspection: diff --git a/mps/manual/source/pool/amcz.rst b/mps/manual/source/pool/amcz.rst index 4f65d205d1b..755a12b8273 100644 --- a/mps/manual/source/pool/amcz.rst +++ b/mps/manual/source/pool/amcz.rst @@ -3,7 +3,7 @@ `<https://info.ravenbrook.com/project/mps/master/manual/wiki/pool_classes.html>`_ .. index:: - single: AMCZ + single: AMCZ pool class single: pool class; AMCZ .. _pool-amcz: @@ -27,7 +27,7 @@ See :ref:`guide-advanced-segregation` for an example. .. index:: - single: AMCZ; properties + single: AMCZ pool class; properties AMCZ properties --------------- @@ -45,7 +45,7 @@ AMCZ is identical to :ref:`pool-amc`, except that: .. index:: - single: AMCZ; interface + single: AMCZ pool class; interface AMCZ interface -------------- @@ -89,7 +89,7 @@ AMCZ interface .. index:: - pair: AMCZ; introspection + pair: AMCZ pool class; introspection AMCZ introspection ------------------ diff --git a/mps/manual/source/pool/ams.rst b/mps/manual/source/pool/ams.rst index 4d66fdc0fc3..e24725122e7 100644 --- a/mps/manual/source/pool/ams.rst +++ b/mps/manual/source/pool/ams.rst @@ -4,7 +4,7 @@ `<https://info.ravenbrook.com/project/mps/master/design/poolams/>`_ .. index:: - single: AMS + single: AMS pool class single: pool class; AMS .. _pool-ams: @@ -31,7 +31,7 @@ blocks that need to be automatically managed, but cannot be moved. .. index:: - single: AMS; properties + single: AMS pool class; properties AMS properties -------------- @@ -89,7 +89,7 @@ AMS properties .. index:: - single: AMS; interface + single: AMS pool class; interface AMS interface ------------- diff --git a/mps/manual/source/pool/awl.rst b/mps/manual/source/pool/awl.rst index 63eca08635e..4ff510e578a 100644 --- a/mps/manual/source/pool/awl.rst +++ b/mps/manual/source/pool/awl.rst @@ -7,7 +7,7 @@ NB: `<https://info.ravenbrook.com/mail/2002/04/12/15-56-15/0.txt>`_ .. index:: - single: AWL + single: AWL pool class single: pool class; AWL .. _pool-awl: @@ -49,7 +49,7 @@ the user guide for a detailed example of using this pool class. .. index:: - single: AWL; properties + single: AWL pool class; properties AWL properties -------------- @@ -105,7 +105,7 @@ AWL properties .. index:: - pair: AWL; dependent object + pair: AWL pool class; dependent object .. _pool-awl-dependent: @@ -203,7 +203,7 @@ For example:: .. index:: - pair: AWL; protection faults + pair: AWL pool class; protection faults .. _pool-awl-barrier: @@ -252,7 +252,7 @@ following are true: #. The MPS is running on Linux/IA-32 or Windows/IA-32. Extending this list to new (reasonable) operating systems should be tolerable (for - example, OS X/IA-32). Extending this to new processor architectures + example, macOS/IA-32). Extending this to new processor architectures requires more work. #. The processor instruction that is accessing the object is of a @@ -267,7 +267,7 @@ memory access instructions. .. index:: - pair: AWL; cautions + pair: AWL pool class; cautions .. _pool-awl-caution: @@ -299,7 +299,7 @@ example <pool-awl-dependent>` above. .. index:: - single: AWL; interface + single: AWL pool class; interface AWL interface ------------- diff --git a/mps/manual/source/pool/index.rst b/mps/manual/source/pool/index.rst index 587ea01afc0..75ed1321979 100644 --- a/mps/manual/source/pool/index.rst +++ b/mps/manual/source/pool/index.rst @@ -14,7 +14,6 @@ Pool reference awl lo mfs - mv mvff mvt snc diff --git a/mps/manual/source/pool/intro.rst b/mps/manual/source/pool/intro.rst index 17b97cb06a4..171f7f24df2 100644 --- a/mps/manual/source/pool/intro.rst +++ b/mps/manual/source/pool/intro.rst @@ -101,34 +101,34 @@ references (1)`. .. csv-table:: - :header: "Property", ":ref:`AMC <pool-amc>`", ":ref:`AMCZ <pool-amcz>`", ":ref:`AMS <pool-ams>`", ":ref:`AWL <pool-awl>`", ":ref:`LO <pool-lo>`", ":ref:`MFS <pool-mfs>`", ":ref:`MV <pool-mv>`", ":ref:`MVFF <pool-mvff>`", ":ref:`MVT <pool-mvt>`", ":ref:`SNC <pool-snc>`" - :widths: 6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + :header: "Property", ":ref:`AMC <pool-amc>`", ":ref:`AMCZ <pool-amcz>`", ":ref:`AMS <pool-ams>`", ":ref:`AWL <pool-awl>`", ":ref:`LO <pool-lo>`", ":ref:`MFS <pool-mfs>`", ":ref:`MVFF <pool-mvff>`", ":ref:`MVT <pool-mvt>`", ":ref:`SNC <pool-snc>`" + :widths: 6, 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 + Supports :c:func:`mps_alloc`?, no, no, no, no, no, yes, yes, no, no + Supports :c:func:`mps_free`?, no, no, no, no, no, yes, yes, yes, no + Supports allocation points?, yes, yes, yes, yes, yes, no, yes, yes, yes + Manages memory using allocation frames?, no, no, no, no, no, no, no, no, yes + Supports segregated allocation caches?, no, no, no, no, no, 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, 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 + Alignment? [5]_, conf, conf, conf, conf, conf, [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 + Blocks are promoted between generations, yes, yes, no, no, no, ---, ---, ---, --- + Blocks are manually managed? [10]_, no, no, no, no, no, yes, yes, yes, yes + Blocks are scanned? [11]_, yes, no, yes, yes, 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, yes + Blocks may move?, yes, yes, no, no, no, no, no, no, no + Blocks may be finalized?, yes, yes, yes, yes, yes, no, no, no, no + Blocks must be formatted? [11]_, yes, yes, yes, yes, yes, no, no, no, yes + Blocks may use :term:`in-band headers`?, yes, yes, yes, yes, yes, ---, ---, ---, no .. note:: diff --git a/mps/manual/source/pool/lo.rst b/mps/manual/source/pool/lo.rst index 197e8b57138..59c7c2d47af 100644 --- a/mps/manual/source/pool/lo.rst +++ b/mps/manual/source/pool/lo.rst @@ -4,7 +4,7 @@ `<https://info.ravenbrook.com/project/mps/master/design/poollo/>`_ .. index:: - single: LO + single: LO pool class single: pool class; LO .. _pool-lo: @@ -39,7 +39,7 @@ and be protected, it is better to use :ref:`pool-amcz` instead. .. index:: - single: LO; properties + single: LO pool class; properties LO properties ------------- @@ -94,7 +94,7 @@ LO properties .. index:: - single: LO; interface + single: LO pool class; interface LO interface ------------ diff --git a/mps/manual/source/pool/mfs.rst b/mps/manual/source/pool/mfs.rst index c1e13a5479d..189ddd319f3 100644 --- a/mps/manual/source/pool/mfs.rst +++ b/mps/manual/source/pool/mfs.rst @@ -1,5 +1,5 @@ .. index:: - single: MFS + single: MFS pool class single: pool class; MFS .. _pool-mfs: @@ -24,7 +24,7 @@ in the free block. :c:func:`mps_alloc` pops this stack and .. index:: - single: MFS; properties + single: MFS pool class; properties MFS properties -------------- @@ -66,7 +66,7 @@ MFS properties .. index:: - single: MFS; interface + single: MFS pool class; interface MFS interface ------------- @@ -91,11 +91,10 @@ MFS interface keyword argument: * :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. + default 65536) is the :term:`size` of extent that the pool will + request from the :term:`arena`. For efficiency, this should be + much larger than :c:macro:`MPS_KEY_MFS_UNIT_SIZE`, so that many + blocks fit into each extent. For example:: diff --git a/mps/manual/source/pool/mv.rst b/mps/manual/source/pool/mv.rst deleted file mode 100644 index 27e691ed3af..00000000000 --- a/mps/manual/source/pool/mv.rst +++ /dev/null @@ -1,115 +0,0 @@ -.. index:: - single: MV - single: pool class; MV - -.. _pool-mv: - -MV (Manual Variable) -==================== - -**MV** is a general-purpose :term:`manually managed <manual memory -management>` :term:`pool class` that manages :term:`blocks` of -variable size. - - -.. index:: - single: MV; properties - -MV properties -------------- - -* Supports allocation via :c:func:`mps_alloc` and deallocation via - :c:func:`mps_free`. - -* Does not support allocation via :term:`allocation points`. - -* Does not support :term:`allocation frames`. - -* Supports :term:`segregated allocation caches`. - -* There are no garbage collections in this pool. - -* Blocks may not contain :term:`references` to blocks in automatically - managed pools (unless these are registered as :term:`roots`). - -* Allocations may be variable in size. - -* The :term:`alignment` of blocks is configurable. - -* Blocks do not have :term:`dependent objects`. - -* Blocks are not automatically :term:`reclaimed`. - -* Blocks are not :term:`scanned <scan>`. - -* Blocks are not protected by :term:`barriers (1)`. - -* Blocks do not :term:`move <moving garbage collector>`. - -* Blocks may not be registered for :term:`finalization`. - -* Blocks must not belong to an :term:`object format`. - - -.. index:: - single: MV; interface - -MV interface ------------- - -:: - - #include "mpscmv.h" - -.. 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` takes four - optional :term:`keyword arguments`: - - * :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_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_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. - - For example:: - - MPS_ARGS_BEGIN(args) { - MPS_ARGS_ADD(args, MPS_KEY_MEAN_SIZE, 32); - MPS_ARGS_ADD(args, MPS_KEY_MAX_SIZE, 1024); - 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); - - -.. c:function:: mps_pool_class_t mps_class_mv_debug(void) - - A :ref:`debugging <topic-debugging>` version of the MV pool - class. - - When creating a debugging MV pool, :c:func:`mps_pool_create_k` - 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/mps/manual/source/pool/mvff.rst b/mps/manual/source/pool/mvff.rst index 04a7d48603d..e4f45c2bf7f 100644 --- a/mps/manual/source/pool/mvff.rst +++ b/mps/manual/source/pool/mvff.rst @@ -3,7 +3,7 @@ `<https://info.ravenbrook.com/project/mps/master/design/poolmvff/>`_ .. index:: - single: MVFF + single: MVFF pool class single: pool class; MVFF .. _pool-mvff: @@ -44,7 +44,7 @@ need both forms of allocation, use two separate pools. .. index:: - single: MVFF; properties + single: MVFF pool class; properties MVFF properties --------------- @@ -88,7 +88,7 @@ MVFF properties .. index:: - single: MVFF; interface + single: MVFF pool class; interface MVFF interface -------------- @@ -115,12 +115,11 @@ MVFF interface efficient if this is wrong, but nothing will break. * :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 *)``. + :c:macro:`MPS_PF_ALIGN`) is the :term:`alignment` of the + addresses allocated (and freed) in the pool. The minimum + alignment supported by pools of this class is ``sizeof(void *)`` + and the maximum is the arena grain size + (see :c:macro:`MPS_KEY_ARENA_GRAIN_SIZE`). * :c:macro:`MPS_KEY_SPARE` (type :c:type:`double`, default 0.75) is the maximum proportion of memory that the pool will keep diff --git a/mps/manual/source/pool/mvt.rst b/mps/manual/source/pool/mvt.rst index da2ce024baa..e29dd400c86 100644 --- a/mps/manual/source/pool/mvt.rst +++ b/mps/manual/source/pool/mvt.rst @@ -3,7 +3,7 @@ `<https://info.ravenbrook.com/project/mps/master/design/poolmvt/>`_ .. index:: - single: MVT + single: MVT pool class single: pool class; MVT .. _pool-mvt: @@ -17,7 +17,7 @@ variable-sized, unformatted objects. It uses the :dfn:`temporal fit` .. index:: - pair: MVT; temporal fit + pair: MVT pool class; temporal fit single: allocation policy; temporal fit Temporal fit @@ -53,7 +53,7 @@ will pessimize the space performance of MVT. .. index:: - single: MVT; properties + single: MVT pool class; properties MVT properties -------------- @@ -97,7 +97,7 @@ MVT properties .. index:: - single: MVT; interface + single: MVT pool class; interface MVT interface ------------- @@ -115,12 +115,11 @@ MVT interface optional :term:`keyword arguments`: * :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 *)``. + :c:macro:`MPS_PF_ALIGN`) is the :term:`alignment` of the + addresses allocated (and freed) in the pool. The minimum + alignment supported by pools of this class is ``sizeof(void *)`` + and the maximum is the arena grain size + (see :c:macro:`MPS_KEY_ARENA_GRAIN_SIZE`). * :c:macro:`MPS_KEY_MIN_SIZE` (type :c:type:`size_t`, default is :c:macro:`MPS_PF_ALIGN`) is the diff --git a/mps/manual/source/pool/snc.rst b/mps/manual/source/pool/snc.rst index d39a392c614..0c0ce095413 100644 --- a/mps/manual/source/pool/snc.rst +++ b/mps/manual/source/pool/snc.rst @@ -3,7 +3,7 @@ `<https://info.ravenbrook.com/project/mps/doc/2002-06-18/obsolete-mminfo/mmdoc/doc/mps/guide/stack-alloc/>`_ .. index:: - single: SNC + single: SNC pool class single: pool class; SNC .. _pool-snc: @@ -11,11 +11,6 @@ SNC (Stack No Checking) ======================= -.. deprecated:: starting with version 1.111. - - If you need special handling of stack-like allocation, - :ref:`contact us <contact>`. - **SNC** is a :term:`manually managed <manual memory management>` :term:`pool class` that supports a stack-like protocol for allocation and deallocation using :term:`allocation frames` on :term:`allocation @@ -23,14 +18,19 @@ points`. See :ref:`topic-frame`. If :c:func:`mps_ap_frame_pop` is used on an allocation point in an SNC pool (after a corresponding call to :c:func:`mps_ap_frame_push`), then -the objects affected by the pop are effectively declared dead, and may -be reclaimed by the collector. Extant references to such objects from -reachable or *de facto* alive objects are safe, but such other objects -should be dead; that is, such references must never be used. +the objects affected by the pop are assumed to be dead, and are +reclaimed by the collector without checking whether there are any +references to them. + +This pool class is intended to be used to implement stack languages +like Forth and PostScript, where some objects are allocated in stack +frames and are known to be dead when the stack is popped, because the +language can ensure that objects that are kept alive when the stack is +popped are copied to the heap. .. index:: - single: SNC; properties + single: SNC pool class; properties SNC properties -------------- @@ -81,7 +81,7 @@ SNC properties .. index:: - single: SNC; interface + single: SNC pool class; interface SNC interface ------------- diff --git a/mps/manual/source/release.rst b/mps/manual/source/release.rst index 5c938fada90..795f27d75b8 100644 --- a/mps/manual/source/release.rst +++ b/mps/manual/source/release.rst @@ -4,6 +4,178 @@ Release notes ============= +.. _release-notes-1.118: + +Release 1.118.0 +--------------- + +Interface changes +................. + +#. The deprecated pool class MV (Manual Variable), and the deprecated + functions ``mps_mv_free_size`` and ``mps_mv_size`` have been + removed. Use :ref:`pool-mvff` and the generic functions + :c:func:`mps_pool_free_size` and :c:func:`mps_pool_total_size` + instead. + + +.. _release-notes-1.117: + +Release 1.117.0 +--------------- + +New features +............ + +#. On FreeBSD, Linux and macOS, the MPS is now able to run in the + child process after ``fork()``. See :ref:`topic-thread-fork`. + +#. The MPS now supports Windows Vista or later; it no longer supports + Windows XP. (Microsoft's own support for Windows XP `expired in + April 2014`_.) This is so that we can use |InitOnceExecuteOnce|_ to + ensure thread-safe initialization. + + .. _expired in April 2014: https://www.microsoft.com/en-gb/windowsforbusiness/end-of-xp-support + .. |InitOnceExecuteOnce| replace:: ``InitOnceExecuteOnce()`` + .. _InitOnceExecuteOnce: https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-initonceexecuteonce + + +Interface changes +................. + +#. The pool class MV (Manual Variable) is now deprecated. + + +Other changes +............. + +#. References from the MPS's own stack frames no longer :term:`pin + <pinning>` objects allocated by the :term:`client program` in + moving pools, which prevented them from moving. See job003525_. + + .. _job003525: https://www.ravenbrook.com/project/mps/issue/job003525/ + +#. Creation of :term:`arenas` is now thread-safe on Windows. See + job004056_. + + .. _job004056: https://www.ravenbrook.com/project/mps/issue/job004056/ + +#. :ref:`pool-awl` and :ref:`pool-lo` pools now detect (and assert on) + invalid :term:`exact references`. See job004070_. + + .. _job004070: https://www.ravenbrook.com/project/mps/issue/job004070/ + +#. The MPS now compiles without warnings on GCC version 7 with + ``-Wextra``. See job004076_. + + .. _job004076: https://www.ravenbrook.com/project/mps/issue/job004076/ + +#. Deprecated function :c:func:`mps_arena_roots_walk` no longer causes + :c:func:`mps_arena_formatted_objects_walk` to miss some objects. See + job004090_. + + .. _job004090: https://www.ravenbrook.com/project/mps/issue/job004090/ + + +.. _release-notes-1.116: + +Release 1.116.0 +--------------- + +New features +............ + +#. The MPS now measures the mortality of a :term:`generation` each + time it is :term:`collected`, and maintains a moving average. This + means that it is no longer important to provide an accurate + estimate of the mortality when creating a :term:`generation chain` + by calling :c:func:`mps_chain_create`. + +#. The MPS no longer supports Linux 2.4 and 2.5. (These versions used + LinuxThreads_ instead of POSIX threads; all major distributions + have long since ceased to support these versions and so it is no + longer convenient to test against them.) See + :ref:`guide-overview-platforms`. + + .. _LinuxThreads: http://pauillac.inria.fr/~xleroy/linuxthreads/ + +#. New function :c:func:`mps_arena_postmortem` assists with postmortem + debugging. + +#. New function :c:func:`mps_arena_busy` assists debugging of re-entry + errors in dynamic function table callbacks on Windows on x86-64. + + +Interface changes +................. + +#. The pool class :ref:`pool-snc` is no longer deprecated. + +#. Allocation frames are no longer deprecated. See :ref:`topic-frame`. + +#. On Linux and FreeBSD, it is now possible to configure the signals + used to suspend and resume threads. See :ref:`topic-thread-signal`. + + +Other changes +............. + +#. It is now possible to register a :term:`thread` with the MPS + multiple times on OS X, thus supporting the use case where a + program that does not use the MPS is calling into MPS-using code + from multiple threads. (This was already supported on other + platforms.) See job003559_. + + .. _job003559: https://www.ravenbrook.com/project/mps/issue/job003559/ + +#. The function :c:func:`mps_arena_formatted_objects_walk` walks the + :term:`formatted objects` in all :term:`pools`. Previously this was + not implemented for :ref:`pool-ams` pools. See job003738_. + + .. _job003738: https://www.ravenbrook.com/project/mps/issue/job003738/ + +#. Objects in :ref:`pool-snc` pools are no longer scanned after their + :term:`allocation frame` is popped, and so do not keep objects in + automatically managed pools alive. See job003883_. + + .. _job003883: https://www.ravenbrook.com/project/mps/issue/job003883/ + +#. When the MPS :term:`collects` a set of :term:`generations`, it + :term:`condemns <condemned set>` only the :term:`blocks` in those + generations. Previously, it also condemned blocks that happened to + share a region of memory with blocks currently or formerly + allocated in those generations. See job004000_. + + .. _job004000: https://www.ravenbrook.com/project/mps/issue/job004000/ + +#. Memory in :term:`allocation points` no longer contributes to the + decision to start a :term:`garbage collection`, avoiding wasted + work repeatedly collecting generations with very small capacities. + See job004007_. + + .. _job004007: https://www.ravenbrook.com/project/mps/issue/job004007/ + +#. The MPS no longer considers :term:`collecting <collect>` the world + again, without allowing the :term:`client program` to run first. + See job004011_. + + .. _job004011: https://www.ravenbrook.com/project/mps/issue/job004011/ + +#. :term:`Roots` created by :c:func:`mps_root_create_thread_scanned` + no longer cause an assertion failure. See job004036_. + + .. _job004036: https://www.ravenbrook.com/project/mps/issue/job004036/ + +#. The MPS test suite now compiles and passes with GCC 6.1. See job004037_. + + .. _job004037: https://www.ravenbrook.com/project/mps/issue/job004037/ + +#. The MPS no longer passes an uninitialized variable to + :c:func:`thread_swap_exception_ports` on OS X. See job004040_. + + .. _job004040: https://www.ravenbrook.com/project/mps/issue/job004040/ + + .. _release-notes-1.115: Release 1.115.0 @@ -12,6 +184,21 @@ Release 1.115.0 New features ............ +#. The MPS now provides control over the maximum time that operations + within an arena may pause the :term:`client program` for. This can + be specified by the new function :c:func:`mps_arena_pause_time_set` + or by passing the new keyword argument + :c:macro:`MPS_KEY_PAUSE_TIME` to :c:func:`mps_arena_create_k`. The + current value can be retrieved by the new function + :c:func:`mps_arena_pause_time`. + + The maximum pause time defaults to 0.1 seconds. For the old + behaviour (whereby the MPS always returned to the :term:`client + program` as soon as possible), set it to zero. + +#. New supported platforms ``fri3ll`` (FreeBSD, IA-32, Clang/LLVM) + and ``fri6ll`` (FreeBSD, x86-64, Clang/LLVM). + #. 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 @@ -41,14 +228,17 @@ New features :c:func:`mps_root_create_area_tagged` for areas of memory that can be scanned by area scanning functions. + Interface changes ................. +#. The pool class MV (Manual Variable) is no longer deprecated. + #. 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`, +#. The functions ``mps_mv_free_size``, ``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 @@ -60,6 +250,18 @@ Interface changes #. The function :c:func:`mps_root_create_table_masked` is deprecated in favour of :c:func:`mps_root_create_table_tagged`. +#. The :ref:`pool-snc` pool class now implements + :c:func:`mps_pool_total_size` and :c:func:`mps_pool_free_size`. + +#. The (undocumented) reservoir functions + :c:func:`mps_ap_fill_with_reservoir_permit`, + :c:func:`mps_reservoir_available`, :c:func:`mps_reservoir_limit`, + :c:func:`mps_reservoir_limit_set`, and + :c:func:`mps_reserve_with_reservoir_permit`, together with the + ``has_reservoir_permit`` arguments to :c:func:`mps_sac_alloc` and + :c:func:`MPS_SAC_ALLOC_FAST` are now deprecated. + + Other changes ............. @@ -86,6 +288,12 @@ Other changes .. _job003870: https://www.ravenbrook.com/project/mps/issue/job003870/ +#. In the :term:`hot` (production) variety, + :c:func:`mps_pool_free_size` now returns the correct result for + :ref:`pool-awl` and :ref:`pool-lo` pools. See job003884_. + + .. _job003884: https://www.ravenbrook.com/project/mps/issue/job003884/ + #. 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 @@ -100,11 +308,45 @@ Other changes .. _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_. + objects allocated from the :ref:`pool-mfs`, MV (Manual Variable), + and :ref:`pool-mvff` pools. See job003866_. .. _job003866: https://www.ravenbrook.com/project/mps/issue/job003866/ +#. The MPS can now make use of :term:`spare committed memory` even if + it is :term:`mapped` at an unhelpful address, by unmapping it and + remapping at a better address. See job003898_. + + .. _job003898: https://www.ravenbrook.com/project/mps/issue/job003898/ + +#. :c:func:`mps_arena_step` now always considers starting a new + :term:`garbage collection` if the remaining idle time is long + enough to complete it. (Previously, if there was already a + collection in progress when :c:func:`mps_arena_step` was called, it + would finish the collection but not consider starting a new one.) + See job003934_. + + .. _job003934: https://www.ravenbrook.com/project/mps/issue/job003934/ + +#. The MPS no longer carries out :term:`garbage collections` when there + is no collection work to be done. See job003938_. + + .. _job003938: https://www.ravenbrook.com/project/mps/issue/job003938/ + +#. The MPS is less aggressive in its use of hardware memory protection + to maintain :term:`write barrier` to speed up future collections. + This is particularly important for OS X, where memory protection + operations are very expensive. See job003371_ and job003975_. + +#. The MPS coalesces memory protection, reducing the number of system + calls. This markedly improves real run time on operating systems + where memory protection operations are very expensive, such as OS + X, but also has a significant effect on Linux. See job003371_ and + job003975_. + + .. _job003371: http://www.ravenbrook.com/project/mps/issue/job003371/ + .. _job003975: http://www.ravenbrook.com/project/mps/issue/job003975/ + .. _release-notes-1.114: @@ -175,8 +417,9 @@ Interface changes (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`. + in an MV (Manual Variable) 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 @@ -495,7 +738,7 @@ Interface changes :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 +#. The pool classes MV (Manual Variable) and :ref:`pool-snc` are now deprecated. #. Allocation frames are now deprecated. See :ref:`topic-frame`. diff --git a/mps/manual/source/topic/allocation.rst b/mps/manual/source/topic/allocation.rst index aaf8fc60a78..08f33d08771 100644 --- a/mps/manual/source/topic/allocation.rst +++ b/mps/manual/source/topic/allocation.rst @@ -45,6 +45,20 @@ Manual allocation unaligned, it will be rounded up to the pool's :term:`alignment` (unless the pool documentation says otherwise). + .. note:: + + It is tempting to call :c:func:`mps_alloc` with a cast from + the desired pointer type to ``mps_addr_t *``, like this:: + + my_object *obj; + res = mps_alloc((mps_addr_t *)&obj, pool, sizeof *obj); + if (res != MPS_RES_OK) + error(...); + + but this is :term:`type punning`, and its behaviour is not + defined in ANSI/ISO Standard C. See :ref:`topic-interface-pun` + for more details. + .. c:function:: void mps_free(mps_pool_t pool, mps_addr_t addr, size_t size) @@ -633,7 +647,6 @@ never fails). mps_addr_t init; mps_addr_t alloc; mps_addr_t limit; - /* ... private fields ... */ } mps_ap_s; ``init`` is the limit of initialized memory. diff --git a/mps/manual/source/topic/arena.rst b/mps/manual/source/topic/arena.rst index 35cb4cd18c5..b31ea7f4df4 100644 --- a/mps/manual/source/topic/arena.rst +++ b/mps/manual/source/topic/arena.rst @@ -15,8 +15,10 @@ An arena is an object that encapsulates the state of the Memory Pool System, and tells it where to get the memory it manages. You typically start a session with the MPS by creating an arena with :c:func:`mps_arena_create_k` and end the session by destroying it with -:c:func:`mps_arena_destroy`. The only function you might need to call -before making an arena is :c:func:`mps_telemetry_control`. +:c:func:`mps_arena_destroy`. The only functions you might need to call +before making an arena are :term:`telemetry system` functions like +:c:func:`mps_telemetry_set` and the :term:`plinth` function +:c:func:`mps_lib_assert_fail_install`. Before destroying an arena, you must first destroy all objects and data in it, as usual for abstract data types in the MPS. If you can't @@ -139,7 +141,7 @@ Client arenas * :c:macro:`MPS_KEY_ARENA_SIZE` (type :c:type:`size_t`) is its size. - It also accepts two optional keyword arguments: + It also accepts three 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 @@ -150,9 +152,14 @@ Client arenas * :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`. + manage memory internally. It must be a power of 2, and at least + ``sizeof(void *)``. Larger granularity reduces overheads, but + increases :term:`fragmentation` and :term:`retention`. + + * :c:macro:`MPS_KEY_PAUSE_TIME` (type :c:type:`double`, default + 0.1) is the maximum time, in seconds, that operations within the + arena may pause the :term:`client program` for. See + :c:func:`mps_arena_pause_time_set` for details. For example:: @@ -213,7 +220,7 @@ Virtual memory arenas more efficient. When creating a virtual memory arena, :c:func:`mps_arena_create_k` - accepts four optional :term:`keyword arguments` on all platforms: + accepts five 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 @@ -264,7 +271,12 @@ Virtual memory arenas some of it to the operating system for use by other processes. See :c:func:`mps_arena_spare` for details. - A fifth optional :term:`keyword argument` may be passed, but it + * :c:macro:`MPS_KEY_PAUSE_TIME` (type :c:type:`double`, default + 0.1) is the maximum time, in seconds, that operations within the + arena may pause the :term:`client program` for. See + :c:func:`mps_arena_pause_time_set` for details. + + A sixth 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`, @@ -306,11 +318,20 @@ Arena properties .. c:function:: mps_word_t mps_collections(mps_arena_t arena) - Return the number of :term:`flips` that have taken place in an - :term:`arena` since it was created. + Return the number of garbage collections (technically, the number + of :term:`flips`) in which objects might have moved, that have + taken place in an :term:`arena` since it was created. ``arena`` is the arena. + .. note:: + + If you are only using non-moving pool classes like + :ref:`pool-ams`, then :c:func:`mps_collections` will always + return 0. To find out about these collections, consider + enabling garbage collection messages: see + :c:func:`mps_message_type_gc`. + .. c:function:: size_t mps_arena_commit_limit(mps_arena_t arena) @@ -411,12 +432,10 @@ Arena properties operating system. The function :c:func:`mps_arena_committed` may be called whatever - state the the arena is in (:term:`unclamped <unclamped state>`, - :term:`clamped <clamped state>`, or :term:`parked <parked - 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 - estimate the size of the heap. + state the the arena is in. If it is called when the arena is in + the :term:`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 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 :c:func:`mps_arena_committed` − @@ -426,6 +445,73 @@ Arena properties :c:func:`mps_arena_commit_limit`. +.. c:function:: double mps_arena_pause_time(mps_arena_t arena) + + Return the maximum time, in seconds, that operations within the + arena may pause the :term:`client program` for. + + ``arena`` is the arena. + + See :c:func:`mps_arena_pause_time_set` for details. + + +.. c:function:: void mps_arena_pause_time_set(mps_arena_t arena, double pause_time) + + Set the maximum time, in seconds, that operations within an arena + may pause the :term:`client program` for. + + ``arena`` is the arena. + + ``pause_time`` is the new maximum pause time, in seconds. It must + be non-negative. + + The MPS makes more efficient use of processor time when it is + allowed longer pauses, up to the maximum time it takes to collect + the entire arena (see :c:func:`mps_arena_collect`). + + When the pause time is short, the MPS needs to take more slices of + time in order to make :term:`garbage collection` progress, and + make more use of :term:`barriers (1)` to support + :term:`incremental garbage collection`. This increases time + overheads, and especially operating system overheads. + + The pause time may be set to zero, in which case the MPS returns + as soon as it can, without regard for overall efficiency. This + value is suitable for applications that require high + responsiveness, but where overall run time is unimportant. + + For interactive applications, set this to the longest pause that a + user won't notice. The default setting of 100ms is intended for + this kind of application. + + The pause time may be set to infinity, in which case the MPS + completes all outstanding :term:`garbage collection` work before + returning from an operation. The consequence is that the MPS will + be able to save on the overheads due to :term:`incremental garbage + collection`, leading to lower total time spent in collection. This + value is suitable for non-interactive applications where total + time is important. + + The MPS makes a best effort to return to the :term:`client + program` from any operation on the arena within the maximum pause + time, but does not guarantee to do so. This is for three reasons: + + 1. many operations in the MPS necessarily take some minimum amount + time that's logarithmic in the amount of :term:`memory (2)` + being managed (so if you set the maximum pause time to zero, + then every operation will exceed it); + + 2. some operations in the MPS call functions in the :term:`client + program` (for example, the :term:`format methods`), and the MPS + has no control over how long these functions take; + + 3. none of the operating systems supported by the MPS provide + real-time guarantees (for example, the process may have to wait + for :term:`memory (2)` to be :term:`paged in`). + + In other words, the MPS is a “soft” real-time system. + + .. c:function:: size_t mps_arena_reserved(mps_arena_t arena) Return the total :term:`address space` reserved by an @@ -535,7 +621,7 @@ Arena properties Arena states ------------ -An arena is always in one of three states. +An arena is always in one of four states. #. .. index:: single: arena; unclamped state @@ -567,21 +653,41 @@ An arena is always in one of three states. The *parked state* is the same as the clamped state, with the additional constraint that no garbage collections are in progress. +#. .. index:: + single: arena; postmortem state + single: postmortem state + + In the *postmortem state*, incremental collection does not take + place, objects do not move in memory, references do not change, the + staleness of :term:`location dependencies` does not change, and + memory occupied by :term:`unreachable` objects is not recycled. + Additionally, all memory protection is removed, and memory may be + in an inconsistent state. + + .. warning:: + + In this state, memory managed by the arena is not in a + consistent state, and so it is not safe to continue running the + client program. This state is intended for postmortem debugging + only. + + 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 the arena in this state :c:func:`mps_arena_create_k`, :c:func:`mps_arena_clamp`, :c:func:`mps_arena_park`, +============================================ ================================== ============================= =========================== ============================== +State unclamped clamped parked postmortem +============================================ ================================== ============================= =========================== ============================== +Collections may be running? yes yes no yes +New collections may start? yes no no no +Objects may move? yes no no no +Location dependencies may become stale? yes no no no +Memory may be returned to the OS? yes no no no +Safe to continue running? yes yes yes 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_postmortem` :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` -============================================ ================================== ============================= =========================== + :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 @@ -606,7 +712,7 @@ can only be called in this state. Put an :term:`arena` into the :term:`clamped state`. - ``arena`` is the arena to clamp. + ``arena`` is the arena. In the clamped state, no object motion will occur and the staleness of :term:`location dependencies` will not change. All @@ -624,7 +730,7 @@ can only be called in this state. Put an :term:`arena` into the :term:`parked state`. - ``arena`` is the arena to park. + ``arena`` is the arena. While an arena is parked, no object motion will occur and the staleness of :term:`location dependencies` will not change. All @@ -638,14 +744,42 @@ can only be called in this state. .. c:function:: void mps_arena_release(mps_arena_t arena) - Puts an arena into the :term:`unclamped state`. + Put an arena into the :term:`unclamped state`. - ``arena`` is the arena to unclamp. + ``arena`` is the arena. While an arena is unclamped, :term:`garbage collection`, object motion, and other background activity can take place. +.. c:function:: void mps_arena_postmortem(mps_arena_t arena) + + Put an arena into the :term:`postmortem state`. + + ``arena`` is the arena. + + In the postmortem state, incremental collection does not take + place, objects do not move in memory, references do not change, + the staleness of :term:`location dependencies` does not change, + and memory occupied by :term:`unreachable` objects is not + recycled. Additionally, all memory protection is removed, and + memory may be in an inconsistent state. + + .. warning:: + + 1. After calling this function, memory managed by the arena is + not in a consistent state, and so it is no longer safe to + continue running the client program. This function is + intended for postmortem debugging only. + + 2. This function must be called from the thread that holds the + arena lock (if any thread holds it). This is the case if the + program is single-threaded, or if it is called from an MPS + assertion handler. When calling this function from the + debugger, check the stack to see which thread has the MPS + arena lock. + + .. index:: single: garbage collection; running single: collection; running @@ -803,9 +937,10 @@ application. .. index:: pair: arena; introspection + pair: arena; debugging -Arena introspection -------------------- +Arena introspection and debugging +--------------------------------- .. note:: @@ -821,6 +956,51 @@ Arena introspection address belongs. +.. c:function:: mps_bool_t mps_arena_busy(mps_arena_t arena) + + Return true if an :term:`arena` is part of the way through + execution of an operation, false otherwise. + + ``arena`` is the arena. + + .. note:: + + This function is intended to assist with debugging fatal + errors in the :term:`client program`. It is not expected to be + needed in normal use. If you find yourself wanting to use this + function other than in the use case described below, there may + be a better way to meet your requirements: please + :ref:`contact us <contact>`. + + A debugger running on Windows on x86-64 needs to decode the + call stack, which it does by calling a callback that was + previously installed in the dynamic function table using + |RtlInstallFunctionTableCallback|_. If the debugger is entered + while the arena is busy, and if the callback needs to read + from MPS-managed memory, then it may attempt to re-enter the + MPS, which will fail as the MPS is not re-entrant. + + .. |RtlInstallFunctionTableCallback| replace:: ``RtlInstallFunctionTableCallback()`` + .. _RtlInstallFunctionTableCallback: https://msdn.microsoft.com/en-us/library/windows/desktop/ms680595(v=vs.85).aspx + + If this happens, in order to allow the debugger to finish + decoding the call stack, the only remedy is to put the arena + into the :term:`postmortem state`, so that memory is + :term:`unprotected` and objects do not move. So in your + dynamic function table callback, you might write:: + + if (mps_arena_busy(arena)) { + mps_arena_postmortem(arena); + } + + .. warning:: + + This function only gives a reliable result in single-threaded + programs, and in multi-threaded programs where all threads but + one are known to be stopped (as they are when the debugger is + decoding the call stack in the use case described above). + + .. c:function:: mps_bool_t mps_arena_has_addr(mps_arena_t arena, mps_addr_t addr) Test whether an :term:`address` is managed by an :term:`arena`. @@ -850,3 +1030,10 @@ 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`. + + .. seealso:: + + To find out which :term:`pool` the address belongs to, use + :c:func:`mps_addr_pool`, and to find out which :term:`object + format` describes the object at the address, use + :c:func:`mps_addr_fmt`. diff --git a/mps/manual/source/topic/cache.rst b/mps/manual/source/topic/cache.rst index f95b0a7a164..1d722049776 100644 --- a/mps/manual/source/topic/cache.rst +++ b/mps/manual/source/topic/cache.rst @@ -269,7 +269,7 @@ Allocation interface have to be one of the :term:`size classes` of the cache; nor does it have to be aligned. - ``has_reservoir_permit`` should be false. + ``has_reservoir_permit`` is obsolete. Pass false. Returns :c:macro:`MPS_RES_OK` if successful: in this case the address of the allocated block is ``*p_o``. The allocated block @@ -285,26 +285,34 @@ Allocation interface .. note:: - There's also a macro :c:func:`MPS_SAC_ALLOC_FAST` that does - the same thing. The macro is faster, but generates more code - and does less checking. + 1. There's also a macro :c:func:`MPS_SAC_ALLOC_FAST` that does + the same thing. The macro is faster, but generates more + code and does less checking. - .. note:: + 2. The :term:`client program` is responsible for synchronizing + the access to the cache, but if the cache decides to access + the pool, the MPS will properly synchronize with any other + :term:`threads` that might be accessing the same pool. - The :term:`client program` is responsible for synchronizing - the access to the cache, but if the cache decides to access - the pool, the MPS will properly synchronize with any other - :term:`threads` that might be accessing the same - pool. + 3. Blocks allocated through a segregated allocation cache + should only be freed through a segregated allocation cache + with the same class structure. Calling :c:func:`mps_free` + on them can cause :term:`memory leaks`, because the size of + the block might be larger than you think. Naturally, the + cache must also be attached to the same pool. - .. note:: + 4. It is tempting to call :c:func:`mps_sac_alloc` with a cast + from the desired pointer type to ``mps_addr_t *``, like + this:: - Blocks allocated through a segregated allocation cache should - only be freed through a segregated allocation cache with the - same class structure. Calling :c:func:`mps_free` on them can - cause :term:`memory leaks`, because the size of - the block might be larger than you think. Naturally, the cache - must also be attached to the same pool. + my_object *obj; + res = mps_alloc((mps_addr_t *)&obj, sac, sizeof *obj, 0); + if (res != MPS_RES_OK) + error(...); + + but this is :term:`type punning`, and its behaviour is not + defined in ANSI/ISO Standard C. See + :ref:`topic-interface-pun` for more details. .. c:function:: MPS_SAC_ALLOC_FAST(mps_res_t res_v, mps_addr_t *p_v, mps_sac_t sac, size_t size, mps_bool_t has_reservoir_permit) @@ -320,9 +328,7 @@ Allocation interface .. note:: :c:func:`MPS_SAC_ALLOC_FAST` may evaluate its arguments - multiple times, except for ``has_reservoir_permit``, which it - evaluates at most once, and only if it decides to access the - pool. + multiple times. .. c:function:: void mps_sac_free(mps_sac_t sac, mps_addr_t p, size_t size) diff --git a/mps/manual/source/topic/collection.rst b/mps/manual/source/topic/collection.rst index d911172f413..e0c7563b471 100644 --- a/mps/manual/source/topic/collection.rst +++ b/mps/manual/source/topic/collection.rst @@ -55,8 +55,9 @@ you can choose which chain it should use by passing the 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`. +kilobytes) and *initial predicted mortality* (between 0 and 1 +inclusive) of each generation, and passing them to +:c:func:`mps_chain_create`. When the *new size* of a generation exceeds its capacity, the MPS will be prepared to start collecting the chain to which the generation @@ -95,17 +96,29 @@ For example:: } mps_gen_param_s; ``mps_capacity`` is the capacity of the generation, in - :term:`kilobytes`. When the size of the generation - exceeds this, the MPS will be prepared to start collecting it. + :term:`kilobytes`. When the size of the generation exceeds this, + the MPS will be prepared to start collecting it. - ``mps_mortality`` is the predicted mortality of the generation: - the proportion (between 0 and 1) of blocks in the generation that - are expected to be :term:`dead` when the generation is collected. + .. note:: - These numbers are hints to the MPS that it may use to make - decisions about when and what to collect: nothing will go wrong - (other than suboptimal performance) if you make poor - choices. See :ref:`topic-collection-schedule`. + The name *capacity* is somewhat misleading. When a generation + reaches its capacity the MPS may not be able to collect it + immediately (for example because some other generation is + being collected), but this does not prevent allocation into + the generation, and so the size of a generation will often + exceed its capacity. + + ``mps_mortality`` is the initial predicted mortality of the + generation: the proportion (between 0 and 1 inclusive) of bytes in + the generation that are expected to be :term:`dead` when the + generation is collected. + + .. note:: + + This value is only used as an initial estimate. The MPS + measures the mortality each time it collects the generation, + and maintains a moving average. So it is not important to + provide an accurate estimate here. .. c:function:: mps_res_t mps_chain_create(mps_chain_t *chain_o, mps_arena_t arena, size_t gen_count, mps_gen_param_s *gen_params) @@ -187,28 +200,6 @@ survive collection get promoted to generation *g*\+1. If the last generation in the chain is collected, the survivors are promoted into an :term:`arena`\-wide "top" generation. -The predicted mortality is used to estimate how long the collection -will take, and this is used in turn to decide how much work the -collector will do each time it has an opportunity to do some work. The constraints here are: - -#. The :term:`client program` might have specified a limit on the - acceptable length of the pause if the work is being done inside - :c:func:`mps_arena_step`. - -#. The collector needs to keep up with the :term:`client program`: - that is, it has to collect garbage at least as fast as the client - is producing it, otherwise the amount of garbage will grow without - bound. - -With perfect prediction, the collector's work should be smoothly -distributed, with a small maximum pause time. Getting the predicted -mortality wrong leads to "lumpy" distribution of collection work with -a longer maximum pause time. If the predicted mortality is too high, -the collector will start out by taking small time slices and then find -that it has to catch up later by taking larger time slices. If the -predicted mortality is too low, the collector will take larger time -slices up front and then find that it is idle later on. - .. index:: single: garbage collection; start message diff --git a/mps/manual/source/topic/debugging.rst b/mps/manual/source/topic/debugging.rst index 61025e31367..8223badf145 100644 --- a/mps/manual/source/topic/debugging.rst +++ b/mps/manual/source/topic/debugging.rst @@ -11,13 +11,12 @@ Debugging pools =============== -Several :term:`pool classes` have debugging counterparts: +Two :term:`pool classes` have debugging counterparts: ================= ============================== Pool class Debugging counterpart ================= ============================== :ref:`pool-ams` :c:func:`mps_class_ams_debug` -:ref:`pool-mv` :c:func:`mps_class_mv_debug` :ref:`pool-mvff` :c:func:`mps_class_mvff_debug` ================= ============================== diff --git a/mps/manual/source/topic/deprecated.rst b/mps/manual/source/topic/deprecated.rst index 35cddb28582..0ccd58a9dce 100644 --- a/mps/manual/source/topic/deprecated.rst +++ b/mps/manual/source/topic/deprecated.rst @@ -7,10 +7,10 @@ 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. +are currently 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:: @@ -19,21 +19,13 @@ supported interface. makes a difference if we know that someone is using a feature. + .. index:: - single: deprecated interfaces; in version 1.115 + single: deprecated interfaces; in version 1.118 -Deprecated in version 1.115 +Deprecated in version 1.118 ........................... -.. 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_arena_spare_commit_limit(mps_arena_t arena) .. deprecated:: @@ -48,39 +40,35 @@ Deprecated in version 1.115 .. deprecated:: - Use: :c:func:`mps_arena_spare_set` instead. + Use :c:func:`mps_arena_spare_set` instead. Change the :term:`spare commit limit` for an :term:`arena` in terms of :term:`bytes (1)` relative to the current :term:`committed <mapped>` memory. -.. c:function:: size_t mps_mv_free_size(mps_pool_t pool) +.. index:: + single: deprecated interfaces; in version 1.115 + +Deprecated in version 1.115 +........................... + +.. c:function:: mps_res_t mps_ap_fill_with_reservoir_permit(mps_addr_t *p_o, mps_ap_t mps_ap, size_t size) .. 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)`. + Identical to :c:func:`mps_ap_fill`, which should be used + instead. Formerly, this function gave the MPS permission to + draw on the ‘low-memory reservoir’, but this no longer exists. -.. c:function:: size_t mps_mv_size(mps_pool_t pool) +.. c:type:: typedef mps_pool_class_t mps_class_t .. 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. + 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_mvff_free_size(mps_pool_t pool) @@ -139,6 +127,41 @@ Deprecated in version 1.115 is the sum of allocated space and free space. +.. c:function:: mps_res_t mps_reserve_with_reservoir_permit(mps_addr_t *p_o, mps_ap_t mps_ap, size_t size) + + .. deprecated:: + + Identical to :c:func:`mps_reserve`, which should be used + instead. Formerly, this function gave the MPS permission to + draw on the ‘low-memory reservoir’, but this no longer + exists. + + +.. c:function:: void mps_reservoir_limit_set(mps_arena_t arena, size_t size) + + .. deprecated:: + + Has no effect. Formerly, it updated the recommended size of + the ‘low-memory reservoir’, but this no longer exists. + + +.. c:function:: size_t mps_reservoir_limit(mps_arena_t arena) + + .. deprecated:: + + Returns zero. Formerly, it returned the recommended size of + the ‘low-memory reservoir’, but this no longer exists. + + +.. c:function:: size_t mps_reservoir_available(mps_arena_t arena) + + .. deprecated:: + + Returns zero. Formerly, it returned the size of the available + memory in the ‘low-memory reservoir’, but this no longer + exists. + + .. c:function:: mps_res_t mps_root_create_reg(mps_root_t *root_o, mps_arena_t arena, mps_rank_t rank, mps_rm_t rm, mps_thr_t thr, mps_reg_scan_t reg_scan, void *p, size_t s) .. deprecated:: @@ -295,16 +318,15 @@ Deprecated in version 1.115 .. 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) .. deprecated:: - - This function is equivalent to:: + + Use :c:func:`mps_root_create_area_tagged` instead, passing + zero for the ``pattern`` argument. This function is equivalent + to:: mps_root_create_area_tagged(root_o, arena, rank, rm, base, base + size, mps_scan_area_tagged, mask, 0) - - Use :c:func:`mps_root_create_area_masked` instead, passing - zero for the ``pattern`` argument. Register a :term:`root` that consists of a vector of :term:`tagged references` whose pattern is zero. @@ -344,18 +366,12 @@ Deprecated in version 1.115 :ref:`topic-scanning`. - .. note:: - - :term:`Client programs` are not expected to - write scanning functions of this type. The built-in MPS - function :c:func:`mps_stack_scan_ambig` must be used. - .. c:function:: mps_reg_scan_t mps_stack_scan_ambig .. deprecated:: - Use :c:func:`mps_root_create_thread` instead, passing + Use :c:func:`mps_root_create_thread_tagged` instead, passing ``sizeof(mps_word_t) - 1`` for the ``mask`` argument, and ``0`` for the ``pattern`` argument. @@ -504,30 +520,6 @@ Deprecated in version 1.112 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`, diff --git a/mps/manual/source/topic/error.rst b/mps/manual/source/topic/error.rst index 9afbb22ebe0..e79b0d87a76 100644 --- a/mps/manual/source/topic/error.rst +++ b/mps/manual/source/topic/error.rst @@ -265,6 +265,13 @@ this documentation. :c:type:`mps_fmt_t` for this argument. +``format.c: format->poolCount == 0`` + + The client program called :c:func:`mps_fmt_destroy` on a format + that was still being used by a pool. It is necessary to call + :c:func:`mps_pool_destroy` first. + + ``global.c: RingIsSingle(&arena->chainRing)`` The client program called :c:func:`mps_arena_destroy` without @@ -279,7 +286,7 @@ this documentation. It is necessary to call :c:func:`mps_fmt_destroy` first. -``global.c: RingIsSingle(&arena->rootRing)`` +``global.c: RingIsSingle(&arenaGlobals->rootRing)`` The client program called :c:func:`mps_arena_destroy` without destroying all the :term:`roots` belonging to the arena. @@ -293,7 +300,7 @@ this documentation. It is necessary to call :c:func:`mps_thread_dereg` first. -``global.c: RingLength(&arenaGlobals->poolRing) == 5`` +``global.c: RingLength(&arenaGlobals->poolRing) == arenaGlobals->systemPools`` The client program called :c:func:`mps_arena_destroy` without destroying all the :term:`pools` belonging to the arena. @@ -317,7 +324,7 @@ this documentation. :term:`stepper functions`. -``locus.c: chain->activeTraces == TraceSetEMPTY`` +``locus.c: gen->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 @@ -351,9 +358,9 @@ this documentation. ``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. + The client program destroyed a pool without first destroying all + the allocation points created on that pool. The allocation points + must be destroyed first. ``trace.c: ss->rank < RankEXACT`` diff --git a/mps/manual/source/topic/finalization.rst b/mps/manual/source/topic/finalization.rst index 7edadb9e3cc..c0648599ab2 100644 --- a/mps/manual/source/topic/finalization.rst +++ b/mps/manual/source/topic/finalization.rst @@ -194,7 +194,7 @@ Cautions 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 + over the table at an appropriate point and finalize any remaining objects yourself. #. Not all :term:`pool classes` support finalization. In general, only diff --git a/mps/manual/source/topic/format.rst b/mps/manual/source/topic/format.rst index 7db5c8a1178..9210a260d16 100644 --- a/mps/manual/source/topic/format.rst +++ b/mps/manual/source/topic/format.rst @@ -228,7 +228,8 @@ Cautions Therefore, the format methods must be able to be run at any time, including asynchronously or in parallel with the rest of the - program. + program. On POSIX systems, this means that format methods must be + async-signal-safe. #. Format methods must be re-entrant. @@ -243,11 +244,12 @@ Cautions a. call library code; - b. perform a non-local exit (for example, by calling ``longjmp``); + b. perform a non-local exit (for example, by throwing an exception, + or calling :c:func:`longjmp`); - c. call any functions in the MPS other than the fix functions - (:c:func:`mps_fix`, :c:func:`MPS_FIX1`, :c:func:`MPS_FIX12`, and - :c:func:`MPS_FIX2`). + c. call any functions or macros in the MPS other than the fix + macros :c:func:`MPS_FIX1`, :c:func:`MPS_FIX12`, and + :c:func:`MPS_FIX2`. It's permissible to call other functions in the client program, but see :c:func:`MPS_FIX_CALL` for a restriction on passing the @@ -368,6 +370,12 @@ Format methods object format has a non-zero :c:macro:`MPS_KEY_FMT_HEADER_SIZE`. + .. note:: + + The MPS will ask for padding objects of any size aligned to + the pool alignment, no matter what size objects the pool + holds. For example, a pool holding only two-word objects may + still be asked to create padding objects 2048 bytes long. .. c:type:: mps_res_t (*mps_fmt_scan_t)(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) diff --git a/mps/manual/source/topic/frame.rst b/mps/manual/source/topic/frame.rst index aa98e204d92..bf62565d06d 100644 --- a/mps/manual/source/topic/frame.rst +++ b/mps/manual/source/topic/frame.rst @@ -13,11 +13,6 @@ Allocation frames ================= -.. deprecated:: starting with version 1.111. - - If you need special handling of stack-like allocation, - :ref:`contact us <contact>`. - An allocation frame is a marker that can pushed onto an :term:`allocation point` by calling :c:func:`mps_ap_frame_push`, and then popped by calling :c:func:`mps_ap_frame_pop` to indicate that all @@ -27,14 +22,15 @@ dead (in the case of :term:`automatic <automatic memory management>` pools). Allocation frames can be used by the :term:`client program` to -efficiently implement stack-like patterns of allocation. +efficiently implement stack-like patterns of allocation, for example +in implementations of stack languages like Forth and PostScript, where +some objects are allocated in stack frames and die when the stack is +popped. .. note:: - All :term:`pool classes` that support :term:`allocation points` - also support pushing and popping of allocation frames, but only - the :ref:`pool-snc` pool class actually uses these frames to - manage its blocks. + The only :term:`pool class` in the MPS that supports allocation + frames is :ref:`pool-snc`. .. c:type:: mps_frame_t diff --git a/mps/manual/source/topic/index.rst b/mps/manual/source/topic/index.rst index 3f2cb2863bf..b3139c16fb6 100644 --- a/mps/manual/source/topic/index.rst +++ b/mps/manual/source/topic/index.rst @@ -30,4 +30,4 @@ Reference platform porting deprecated - + security diff --git a/mps/manual/source/topic/interface.rst b/mps/manual/source/topic/interface.rst index e7715181836..e12eb4d1f36 100644 --- a/mps/manual/source/topic/interface.rst +++ b/mps/manual/source/topic/interface.rst @@ -115,8 +115,8 @@ Identifiers Types ----- -There are three kinds of types declared in the MPS interface: -*transparent types*, *opaque types*, and *derived types*. +There are two kinds of types declared in the MPS interface: +*transparent types* and *opaque types*. #. A *transparent type* is an alias defined using ``typedef``, and this is documented so that the :term:`client program` can rely on that @@ -138,13 +138,6 @@ There are three kinds of types declared in the MPS interface: scanning macros such as :c:func:`MPS_SCAN_BEGIN` and :c:func:`MPS_FIX12`. -#. A *derived type* is a structure or function type based on - transparent and opaque types and on built-in C types. The degree - to which you may or must depend upon the implementation of a - derived type is covered by the documentation for the type. For - example, the structure type :c:type:`mps_ap_s` has a mixture of - public and private members. - .. index:: single: interface; functions diff --git a/mps/manual/source/topic/keyword.rst b/mps/manual/source/topic/keyword.rst index aa437b87b28..7dadee60b66 100644 --- a/mps/manual/source/topic/keyword.rst +++ b/mps/manual/source/topic/keyword.rst @@ -86,7 +86,7 @@ now :c:macro:`MPS_KEY_ARGS_END`. 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_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_GRAIN_SIZE` :c:type:`size_t` ``size`` :c:func:`mps_arena_class_vm`, :c:func:`mps_arena_class_cl` @@ -94,7 +94,7 @@ now :c:macro:`MPS_KEY_ARGS_END`. :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_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_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` @@ -106,8 +106,7 @@ now :c:macro:`MPS_KEY_ARGS_END`. :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_MEAN_SIZE` :c:type:`size_t` ``size`` :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` @@ -115,6 +114,7 @@ now :c:macro:`MPS_KEY_ARGS_END`. :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_PAUSE_TIME` :c:type:`double` ``d`` :c:func:`mps_arena_class_vm`, :c:func:`mps_arena_class_cl` :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_arena_class_vm`, :c:func:`mps_class_mvff` diff --git a/mps/manual/source/topic/location.rst b/mps/manual/source/topic/location.rst index fa928756a69..aa70a071683 100644 --- a/mps/manual/source/topic/location.rst +++ b/mps/manual/source/topic/location.rst @@ -77,8 +77,8 @@ the function :c:func:`mps_ld_reset`. .. note:: - This means that it is not possible to statically create a location - dependency that has been reset. + It is not possible to statically create a location dependency that + has been reset. You can call :c:func:`mps_ld_reset` at any later point to clear all dependencies from the structure. For example, this is normally done diff --git a/mps/manual/source/topic/platform.rst b/mps/manual/source/topic/platform.rst index e74aa34cde2..0538ec52f16 100644 --- a/mps/manual/source/topic/platform.rst +++ b/mps/manual/source/topic/platform.rst @@ -20,7 +20,7 @@ six-character code breaks down into three pairs of characters: ``fr`` FreeBSD :c:macro:`MPS_OS_FR` ``li`` Linux :c:macro:`MPS_OS_LI` ``w3`` Windows :c:macro:`MPS_OS_W3` -``xc`` OS X :c:macro:`MPS_OS_XC` +``xc`` macOS :c:macro:`MPS_OS_XC` ====== ================ ==================== The second pair of characters names the processor architecture: @@ -126,7 +126,7 @@ Platform interface .. c:macro:: MPS_OS_XC A :term:`C` preprocessor macro that indicates, if defined, that - the MPS was compiled on an OS X operating system. + the MPS was compiled on an macOS operating system. .. c:macro:: MPS_PF_ALIGN @@ -142,6 +142,13 @@ Platform interface IA-32 processor architecture, and the GCC compiler. +.. c:macro:: MPS_PF_FRI3LL + + A :term:`C` preprocessor macro that indicates, if defined, that + the :term:`platform` consists of the FreeBSD operating system, the + IA-32 processor architecture, and the Clang/LLVM compiler. + + .. c:macro:: MPS_PF_FRI6GC A :term:`C` preprocessor macro that indicates, if defined, that @@ -149,6 +156,13 @@ Platform interface x86-64 processor architecture, and the GCC compiler. +.. c:macro:: MPS_PF_FRI6LL + + A :term:`C` preprocessor macro that indicates, if defined, that + the :term:`platform` consists of the FreeBSD operating system, the + x86-64 processor architecture, and the Clang/LLVM compiler. + + .. c:macro:: MPS_PF_LII3GC A :term:`C` preprocessor macro that indicates, if defined, that @@ -195,21 +209,28 @@ Platform interface .. c:macro:: MPS_PF_XCI3GC A :term:`C` preprocessor macro that indicates, if defined, that - the :term:`platform` consists of the OS X operating system, the + the :term:`platform` consists of the macOS operating system, the IA-32 processor architecture, and the GCC compiler. .. c:macro:: MPS_PF_XCI3LL A :term:`C` preprocessor macro that indicates, if defined, that - the :term:`platform` consists of the OS X operating system, the + the :term:`platform` consists of the macOS operating system, the IA-32 processor architecture, and the Clang/LLVM compiler. +.. c:macro:: MPS_PF_XCI6GC + + A :term:`C` preprocessor macro that indicates, if defined, that + the :term:`platform` consists of the macOS operating system, the + x86-64 processor architecture, and the GCC compiler. + + .. c:macro:: MPS_PF_XCI6LL A :term:`C` preprocessor macro that indicates, if defined, that - the :term:`platform` consists of the OS X operating system, the + the :term:`platform` consists of the macOS operating system, the x86-64 processor architecture, and the Clang/LLVM compiler. @@ -328,8 +349,10 @@ the Memory Pool System, with their current status. Platform Status ========== ======================= ``fri3gc`` Supported +``fri3ll`` Supported ``fri4gc`` Corrected to ``fri3gc`` ``fri6gc`` Supported +``fri6ll`` Supported ``i5m2cc`` *Not supported* ``iam4cc`` *Not supported* ``lii3eg`` *Not supported* @@ -357,6 +380,7 @@ Platform Status ``w3ppmv`` *Not supported* ``xci3gc`` *Not supported* ``xci3ll`` Supported +``xci6gc`` *Not supported* ``xci6ll`` Supported ``xcppgc`` *Not supported* ========== ======================= diff --git a/mps/manual/source/topic/plinth.rst b/mps/manual/source/topic/plinth.rst index f771f6c1c31..780e12be7e6 100644 --- a/mps/manual/source/topic/plinth.rst +++ b/mps/manual/source/topic/plinth.rst @@ -72,7 +72,7 @@ for useful advice.) If this preprocessor constant is defined, exclude the ANSI plinth (``mpsioan.c`` and ``mpsliban.c``) from the MPS. For example:: - cc -DCONFIG_PLINTH_NONE -c mps.c (Unix/OS X) + cc -DCONFIG_PLINTH_NONE -c mps.c (Unix/macOS) cl /Gs /DCONFIG_PLINTH_NONE /c mps.c (Windows) Having excluded the ANSI plinth, you must of course supply your @@ -468,8 +468,8 @@ Library module A :term:`plinth` function to supply a default value for the :term:`telemetry filter` from the environment. See - :c:func:`mps_telemetry_control` for more information on the - significant of the value. + :envvar:`MPS_TELEMETRY_CONTROL` for more information on the + significance of the value. Returns the default value of the telemetry filter, as derived from the environment. It is recommended that the environment be diff --git a/mps/manual/source/topic/pool.rst b/mps/manual/source/topic/pool.rst index d1ffe56d45b..360911494c5 100644 --- a/mps/manual/source/topic/pool.rst +++ b/mps/manual/source/topic/pool.rst @@ -163,3 +163,10 @@ Pool introspection may immediately become invalidated. For reliable results call this function and interpret the result while the arena is in the :term:`parked state`. + + .. seealso:: + + To find out which :term:`object format` describes the object + at the address, use :c:func:`mps_addr_fmt`. If you only care + whether the address belongs to a particular :term:`arena`, use + :c:func:`mps_arena_has_addr`. diff --git a/mps/manual/source/topic/porting.rst b/mps/manual/source/topic/porting.rst index 0d04c04f3a4..3dd0e69d172 100644 --- a/mps/manual/source/topic/porting.rst +++ b/mps/manual/source/topic/porting.rst @@ -48,8 +48,8 @@ usable. 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``. + interface. There are implementations for 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. @@ -61,23 +61,22 @@ usable. 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``. + ``protsgix.c``, Windows in ``protw3.c``, and macOS using Mach in + ``protix.c`` plus ``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. + 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. +#. The **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 + See :ref:`design-prmc` for the design, and ``prmc.h`` for the + interface. There are implementations on Unix, Windows, and macOS for IA-32 and x86-64. There is a generic implementation in ``prmcan.c``, which can't @@ -98,18 +97,14 @@ usable. 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. + :term:`registers` and :term:`control stack` of the thread that + entered the MPS. - 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. + See :ref:`design-stack-scan` for the design, ``ss.h`` for the + interface, and ``ss.c`` for a generic implementation that makes + assumptions about the platform (in particular, that the stack grows + downwards and :c:func:`setjmp` reliably captures the registers; see + the design for details). #. The **thread manager** module suspends and resumes :term:`threads`, so that the MPS can gain exclusive access to :term:`memory (2)`, @@ -118,7 +113,7 @@ usable. 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 + plus ``pthrdext.c``, macOS using Mach in ``thxc.c``, Windows in ``thw3.c``. There is a generic implementation in ``than.c``, which necessarily @@ -192,20 +187,20 @@ 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 */ + /* Linux on x86-64 with GCC or Clang */ #elif defined(MPS_PF_LII6GC) || defined(MPS_PF_LII6LL) - #include "lockli.c" /* Linux locks */ + #include "lockix.c" /* Posix 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 "protsgix.c" /* Posix signal handling */ + #include "prmci6.c" /* x86-64 mutator context */ + #include "prmcix.c" /* Posix mutator context */ + #include "prmclii6.c" /* x86-64 for Linux mutator context */ #include "span.c" /* generic stack probe */ - #include "ssixi6.c" /* Posix on 64-bit Intel stack scan */ Makefile @@ -229,14 +224,14 @@ For example, ``lii6ll.gmk`` looks like this: PFM = lii6ll MPMPF = \ - lockli.c \ - prmci6li.c \ - proti6.c \ + lockix.c \ + prmci6.c \ + prmcix.c \ + prmclii6.c \ protix.c \ - protli.c \ + protsgix.c \ pthrdext.c \ span.c \ - ssixi6.c \ thix.c \ vmix.c @@ -267,13 +262,12 @@ this: MPMPF = \ [lockw3] \ [mpsiw3] \ - [prmci6w3] \ - [proti6] \ + [prmci6] \ + [prmcw3] \ + [prmcw3i6] \ [protw3] \ [spw3i6] \ - [ssw3i6mv] \ [thw3] \ - [thw3i6] \ [vmw3] !INCLUDE commpre.nmk diff --git a/mps/manual/source/topic/root.rst b/mps/manual/source/topic/root.rst index c174568b446..a316149c610 100644 --- a/mps/manual/source/topic/root.rst +++ b/mps/manual/source/topic/root.rst @@ -511,8 +511,9 @@ Root interface :c:func:`mps_scan_area`, or a similar user-defined function. See :ref:`topic-scanning-area`. - ``closure`` is an arbitrary pointer that will be passed to ``scan_area`` - and intended to point to any parameters it needs. + ``closure`` is an arbitrary pointer that will be passed to + ``scan_area`` and is intended to point to any parameters it needs. + Ensure anything it points to exists as long as the root exists. ``cold`` is a pointer to the :term:`cold end` of stack to be scanned. On platforms where the stack grows downwards (currently, @@ -543,7 +544,8 @@ Root interface ``base`` points to the first word to be scanned. - ``limit`` points to the location just beyond the end of the area to be scanned. + ``limit`` points to the location just beyond the end of the area + to be scanned. ``scan_area`` is an area scanning function, for example :c:func:`mps_scan_area`, or a similar user-defined function. See @@ -551,6 +553,7 @@ Root interface ``closure`` is an arbitrary pointer that will be passed to ``scan_area`` and intended to point to any parameters it needs. + Ensure anything it points to exists as long as the root exists. Returns :c:macro:`MPS_RES_OK` if the root was registered successfully, :c:macro:`MPS_RES_MEMORY` if the new root @@ -576,12 +579,14 @@ Root interface ``base`` points to a vector of tagged references. - ``count`` is the number of tagged references in the vector. + ``limit`` points to the location just beyond the end of the vector + of tagged references. ``scan_area`` is an tagged area scanning function that will be used to scan the area, for example :c:func:`mps_scan_area_tagged` - or :c:func:`mps_scan_area_tagged_or_zero`. See - :ref:`topic-scanning-area`. + or :c:func:`mps_scan_area_tagged_or_zero`. The ``closure`` + argument to ``scan_area`` is a :c:type:`mps_scan_tag_t` cast to + ``void *`` See :ref:`topic-scanning-area`. ``mask`` is a :term:`bitmask` that is passed to ``scan_area`` to be applied to the words in the vector to locate the :term:`tag`. diff --git a/mps/manual/source/topic/security.rst b/mps/manual/source/topic/security.rst new file mode 100644 index 00000000000..16a148b2a8b --- /dev/null +++ b/mps/manual/source/topic/security.rst @@ -0,0 +1,98 @@ +.. index:: + single: security issues + +.. _topic-security: + +Security issues +=============== + +This chapter describes security issues that may be present when using +the MPS. + + +.. index:: + pair: security issues; predictable address space layout on FreeBSD + single: address space; predictable layout on FreeBSD + +Predictable address space layout on FreeBSD +------------------------------------------- + +The MPS acquires :term:`address space` using the operating system's +:term:`virtual memory` interface (specifically, :c:func:`mmap` on +FreeBSD). As of version 10, FreeBSD does not randomize the allocated +regions of address space, which means that the :term:`addresses` of +:term:`blocks` allocated by the MPS are predictable: a :term:`client +program` that makes an identical series of calls to the MPS gets an +identical series of addresses back. + +This means that if a program using the MPS has a buffer overflow, the +overflow is more easily exploitable by an attacker than if the program +had used :c:func:`malloc` (which has some randomization of the +allocated addresses), because it is easier for an attacker to +determine the address of allocated structures. + +There is currently no workaround for this issue. If this affects you, +please :ref:`contact us <contact>`. + +Other supported platforms are unaffected by this issue: Linux and macOS +randomize the addresses allocated by :c:func:`mmap`, and Windows +randomizes the addresses allocated by :c:func:`VirtualAlloc`. + + +.. index:: + pair: security issues; address disclosure + +Address disclosure +------------------ + +The MPS supports :term:`semi-conservative garbage collection` in which +some memory locations are :term:`scanned <scan>` as :term:`ambiguous +references`. This may make it possible for a program to discover the +:term:`address` of an :term:`object`, even if the programming language +has no feature for obtaining the address of an object. Discovering the +addresses of objects makes it easier to exploit buffer overflow bugs. + +The attack proceeds as follows: create a :term:`weak reference (1)` to +the object of interest (for example, via a :term:`weak-key hash +table`); guess a value for the address of the object; and arrange for +that value to be scanned as an ambiguous reference (for example, by +ensuring that it appears in :term:`registers` or on the :term:`control +stack` of a :term:`thread`). If the guess was correct, the MPS keeps +the object :term:`alive <live>`; if incorrect, the object may +:term:`die <dead>`. The attacker can then determine which of these was +the case by examining the weak reference to see if it has been +:term:`splatted <splat>`. + +The attack was pointed out by `Dionysus Blazakis in 2012 +<https://github.com/justdionysus/gcwoah>`_ with respect to JavaScript +implementations, but it affects all :term:`conservative <conservative +garbage collection>` and :term:`semi-conservative <semi-conservative +garbage collection>` garbage collectors. + + +.. index:: + pair: security issues; telemetry + +Telemetry +--------- + +In its :term:`hot` and :term:`cool` varieties, the MPS contains a +:term:`telemetry system` which can be configured to record a stream of +events for later analysis and debugging. When using the default +:term:`plinth`, the behaviour of the telemetry system is under the +control of the environment variable :envvar:`MPS_TELEMETRY_CONTROL`, +and the telemetry stream is written to the file named by the +environment variable :envvar:`MPS_TELEMETRY_FILENAME`. + +This means that an attacker who can set arbitrary environment +variables when running a program that uses the MPS can cause that +program to write a telemetry stream to an arbitrary file. This +behaviour might be unexpected, and might enable a data overwriting +attack, or a denial-of-service attack, since telemetry streams are +typically very large. + +If this is an issue for your program, then you can modify or replace +the :ref:`topic-plinth-io` in the :term:`plinth` so that it meets your +requirements, or distribute the :term:`rash` variety of the MPS, which +omits the :term:`telemetry system` entirely, and use the other +varieties only for development and testing. diff --git a/mps/manual/source/topic/telemetry.rst b/mps/manual/source/topic/telemetry.rst index 5ee6bb000e1..09445c9a66c 100644 --- a/mps/manual/source/topic/telemetry.rst +++ b/mps/manual/source/topic/telemetry.rst @@ -125,14 +125,14 @@ as the `Time Stamp Counter <https://en.wikipedia.org/wiki/Time_Stamp_Counter>`_ on IA-32 and x86-64, if one is available. All numbers are given in hexadecimal. :: - 000AE03973336E3C 002B VMCreate vm:00000001003FC000 base:00000001003FD000 limit:00000001003FE000 - 000AE0397333BC6D 002D VMMap vm:00000001003FC000 base:00000001003FD000 limit:00000001003FE000 - 000AE0397334DF9F 001A Intern stringId:0000000000000002 string:"Reservoir" - 000AE0397334E0A0 001B Label address:00000001078C85B8["Reservoir"] stringId:0000000000000002 - 000AE03973352375 0015 PoolInit pool:00000001003FD328 arena:00000001003FD000 poolClass:00000001078C85B8["Reservoir"] - 000AE039733592F9 002B VMCreate vm:00000001003FE000 base:00000001003FF000 limit:000000010992F000 - 000AE0397335C8B5 002D VMMap vm:00000001003FE000 base:00000001003FF000 limit:0000000107930000 - 000AE03973361D5A 0005 ArenaCreateVM arena:00000001003FD000 userSize:0000000002000000 chunkSize:0000000002000000 + 000021C9DB3812C7 0075 EventClockSync clock:0000000000001EE3 + 000021C9DB39E2FB 002B VMInit vm:00007FFF5429C4B8 base:000000010BA4A000 limit:000000010BA4B000 + 000021C9DB3A5630 002D VMMap vm:00007FFF5429C4B8 base:000000010BA4A000 limit:000000010BA4B000 + 000021C9DB3E6BAA 001A Intern stringId:0000000000000002 string:"MFS" + 000021C9DB3E6E17 001B Label address:000000010BA0C5D8["MFS"] stringId:0000000000000002 + 000021C9DB3EB6F8 0044 PoolInitMFS pool:000000010BA4A360 arena:000000010BA4A000 extendBy:0000000000001000 extendSelf:False unitSize:0000000000000030 + 000021C9DB3EFE3B 002B VMInit vm:00007FFF5429C3D0 base:000000010BC84000 limit:000000010CC24000 + 000021C9DB3F33F3 002D VMMap vm:00007FFF5429C3D0 base:000000010BC84000 limit:000000010BC85000 You can search through the telemetry for events related to particular addresses of interest. @@ -269,14 +269,14 @@ Here's some example output. The first column contains the timestamp of the event, the second column contains the event type, and remaining columns contain parameters related to the event. :: - 000AE03973336E3C 2B 1003FC000 1003FD000 1003FE000 - 000AE0397333BC6D 2D 1003FC000 1003FD000 1003FE000 - 000AE0397334DF9F 1A 2 "Reservoir" - 000AE0397334E0A0 1B 1078C85B8 2 - 000AE03973352375 15 1003FD328 1003FD000 1078C85B8 - 000AE039733592F9 2B 1003FE000 1003FF000 10992F000 - 000AE0397335C8B5 2D 1003FE000 1003FF000 107930000 - 000AE03973361D5A 5 1003FD000 2000000 2000000 + 000021C9DB3812C7 75 1EE3 + 000021C9DB39E2FB 2B 7FFF5429C4B8 10BA4A000 10BA4B000 + 000021C9DB3A5630 2D 7FFF5429C4B8 10BA4A000 10BA4B000 + 000021C9DB3E6BAA 1A 2 "MFS" + 000021C9DB3E6E17 1B 10BA0C5D8 2 + 000021C9DB3EB6F8 44 10BA4A360 10BA4A000 1000 0 30 + 000021C9DB3EFE3B 2B 7FFF5429C3D0 10BC84000 10CC24000 + 000021C9DB3F33F3 2D 7FFF5429C3D0 10BC84000 10BC85000 .. index:: @@ -306,14 +306,14 @@ takes the following options: For example, here's the result of passing the output shown above through :program:`mpseventtxt`:: - 000AE03973336E3C 002B VMCreate vm:00000001003FC000 base:00000001003FD000 limit:00000001003FE000 - 000AE0397333BC6D 002D VMMap vm:00000001003FC000 base:00000001003FD000 limit:00000001003FE000 - 000AE0397334DF9F 001A Intern stringId:0000000000000002 string:"Reservoir" - 000AE0397334E0A0 001B Label address:00000001078C85B8["Reservoir"] stringId:0000000000000002 - 000AE03973352375 0015 PoolInit pool:00000001003FD328 arena:00000001003FD000 poolClass:00000001078C85B8["Reservoir"] - 000AE039733592F9 002B VMCreate vm:00000001003FE000 base:00000001003FF000 limit:000000010992F000 - 000AE0397335C8B5 002D VMMap vm:00000001003FE000 base:00000001003FF000 limit:0000000107930000 - 000AE03973361D5A 0005 ArenaCreateVM arena:00000001003FD000 userSize:0000000002000000 chunkSize:0000000002000000 + 000021C9DB3812C7 0075 EventClockSync clock:0000000000001EE3 + 000021C9DB39E2FB 002B VMInit vm:00007FFF5429C4B8 base:000000010BA4A000 limit:000000010BA4B000 + 000021C9DB3A5630 002D VMMap vm:00007FFF5429C4B8 base:000000010BA4A000 limit:000000010BA4B000 + 000021C9DB3E6BAA 001A Intern stringId:0000000000000002 string:"MFS" + 000021C9DB3E6E17 001B Label address:000000010BA0C5D8["MFS"] stringId:0000000000000002 + 000021C9DB3EB6F8 0044 PoolInitMFS pool:000000010BA4A360 arena:000000010BA4A000 extendBy:0000000000001000 extendSelf:False unitSize:0000000000000030 + 000021C9DB3EFE3B 002B VMInit vm:00007FFF5429C3D0 base:000000010BC84000 limit:000000010CC24000 + 000021C9DB3F33F3 002D VMMap vm:00007FFF5429C3D0 base:000000010BC84000 limit:000000010BC85000 .. index:: @@ -491,9 +491,10 @@ used in queries, for example: .. note:: If the ``User`` event category is not turned on in the - :term:`telemetry filter` (via :c:func:`mps_telemetry_control`) - then the string is not sent to the telemetry stream. A label - is still returned in this case, but it is useless. + :term:`telemetry filter` (via :c:func:`mps_telemetry_set` or + :envvar:`MPS_TELEMETRY_CONTROL`) then the string is not sent + to the telemetry stream. A label is still returned in this + case, but it is useless. .. c:function:: void mps_telemetry_label(mps_addr_t addr, mps_label_t label) @@ -512,8 +513,9 @@ used in queries, for example: .. note:: 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. + :term:`telemetry filter` (via :c:func:`mps_telemetry_set` or + :envvar:`MPS_TELEMETRY_CONTROL`) then calling this function + has no effect. .. index:: diff --git a/mps/manual/source/topic/thread.rst b/mps/manual/source/topic/thread.rst index 0697daac301..056a9dbce34 100644 --- a/mps/manual/source/topic/thread.rst +++ b/mps/manual/source/topic/thread.rst @@ -44,8 +44,7 @@ access that memory. This means that threads must be registered with the MPS by calling :c:func:`mps_thread_reg` (and thread roots created; see :ref:`topic-root-thread`). -For simplicity, we recommend that a thread must be registered with an -:term:`arena` if: +A thread must be registered with an :term:`arena` if: * its :term:`control stack` and :term:`registers` form a root (this is enforced by :c:func:`mps_root_create_thread`); or @@ -70,17 +69,30 @@ Signal and exception handling issues .. warning:: - On Unix platforms (except OS X), the MPS suspends and resumes - threads by sending them signals. There's a shortage of available - signals that aren't already dedicated to other purposes (for - example, ValGrind uses ``SIGUSR1`` and ``SIGUSR2``), so the MPS uses - ``SIGXCPU`` and ``SIGXFSZ``. This means that programs must not mask - these two signals. + On Linux and FreeBSD, the MPS suspends and resumes threads by + sending them signals. There's a shortage of available signals that + aren't already dedicated to other purposes (for example, ValGrind + uses ``SIGUSR1`` and ``SIGUSR2``), so the MPS uses ``SIGXCPU`` and + ``SIGXFSZ``. This means that programs must not mask or handle + either of these signals. - If your program needs to handle these signals, then it must - co-operate with the MPS. At present, there's no documented - mechanism for co-operating: if you are in this situation, please - :ref:`contact us <contact>`. + If your program needs to mask or handle either of these signals, + then you can configure the MPS to use another pair of signals of + your choosing, by defining these preprocessor constants: + + .. c:macro:: CONFIG_PTHREADEXT_SIGSUSPEND + + If this preprocessor constant is defined, its definition names + the signal used to suspend a thread. For example:: + + cc -DCONFIG_PTHREADEXT_SIGSUSPEND=SIGUSR1 -c mps.c + + .. c:macro:: CONFIG_PTHREADEXT_SIGRESUME + + If this preprocessor constant is defined, its definition names + the signal used to resume a thread. For example:: + + cc -DCONFIG_PTHREADEXT_SIGSUSPEND=SIGUSR2 -c mps.c .. warning:: @@ -92,7 +104,7 @@ Signal and exception handling issues * On Windows, you must not install a first-chance exception handler. - * On OS X, you must not install a thread-local Mach exception handler + * On macOS, you must not install a thread-local Mach exception handler for ``EXC_BAD_ACCESS`` exceptions. All of these things are, in fact, possible, but your program must @@ -101,6 +113,43 @@ Signal and exception handling issues us <contact>`. +.. index:: + single: fork safety + +.. _topic-thread-fork: + +Fork safety +----------- + +On Linux, FreeBSD and macOS, the MPS makes a best-effort attempt to +continue running in the child process after a call to :c:func:`fork`, +even if the :term:`client program` was running multiple +:term:`threads` at the point where the call is made to :c:func:`fork`. + +.. warning:: + + POSIX offers little or nothing in the way of guarantees about the + situation of a child process running after a multi-threaded parent + forked. The specification_ says: + + .. _specification: http://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html + + A process shall be created with a single thread. If a + multi-threaded process calls :c:func:`fork`, the new process shall + contain a replica of the calling thread and its entire address + space, possibly including the states of mutexes and other + resources. Consequently, to avoid errors, the child process may + only execute async-signal-safe operations until such time as one + of the :c:func:`exec` functions is called. + +.. note:: + + Although only one thread is created in the child process, any + threads in the parent process that were registered with the MPS by + calling :c:func:`mps_thread_reg` must still be deregistered, by + calling :c:func:`mps_thread_dereg`, before the arena is destroyed. + + .. index:: single: thread; interface diff --git a/mps/procedure/release-build.rst b/mps/procedure/release-build.rst index 66a56fba2f8..d6b2077e725 100644 --- a/mps/procedure/release-build.rst +++ b/mps/procedure/release-build.rst @@ -28,16 +28,15 @@ All relative paths are relative to ---------------- #. Make sure you have a version branch from which to make the release. - If not, follow the `version creation procedure <version-create>`_ - first. + If not, follow the `version creation procedure`_ first. - .. _version-create: version-create + .. _version creation procedure: version-create -#. Make sure that you have rights to push to the ``mps`` - repository on GitHub. If not, follow the `Becoming a Ravenbrook - team member procedure <git-fusion>`_ first. +#. Make sure that you can authenticate to Git Fusion, and that you + have rights to push to the ``mps`` repository on GitHub. If not, + follow the `Git Fusion procedures`_ first. - .. _git-fusion: https://info.ravenbrook.com/procedure/git-fusion + .. _Git Fusion procedures: https://info.ravenbrook.com/procedure/git-fusion 3. Setting up for release @@ -79,14 +78,24 @@ All relative paths are relative to procedure:: p4 opened version/$VERSION/... - # should output "version/$VERSION/... - file(s) not opened on this client." + + This should output "version/$VERSION/... - file(s) not opened on + this client." But if there are opened files, then:: + p4 revert version/$VERSION/... + + Next:: + + p4 update version/$VERSION/...@$CHANGELEVEL + p4 status version/$VERSION/... + + This should output "version/$VERSION/... - no file(s) to + reconcile." But if there are discrepancies, then:: + rm -rf version/$VERSION p4 sync -f version/$VERSION/...@$CHANGELEVEL - Note that the ``revert`` and ``sync -f`` are necessary, otherwise - opened files may be left in place, or writeable-on-client files may - be omitted; see [RHSK_2008-10-16]_. + See [RHSK_2008-10-16]_. #. Run the test suite:: @@ -98,9 +107,11 @@ All relative paths are relative to commands to run the test suite are:: cd version\$VERSION\code - nmake /f w3i6mv.nmk testrun - - On other platforms they are as shown above. + nmake /f w3i6mv.nmk clean testci + nmake /f ananmv.nmk clean testansi + nmake /f ananmv.nmk CFLAGS="-DCONFIG_POLL_NONE" clean testpollnone + cd ../test + perl test/qa runset testsets/{coolonly,argerr,conerr,passing} #. Check that there are no performance regressions by comparing the benchmarks (``djbench`` and ``gcbench``) for the last release and @@ -122,7 +133,7 @@ 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:: - tool/release -b version/1.113 -d "Improved interface to generation chains." -y + tool/release -b version/$VERSION -d "Improved interface to generation chains." -y 6. Making the release (manual procedure) @@ -134,7 +145,7 @@ the branch. A typical invocation looks like this:: release name according to the variant, for example, ``mps-cet-1.110.0.zip`` -On a Unix (including OS X) machine: +On a Unix (including macOS) machine: #. Create a fresh Perforce client workspace:: @@ -212,11 +223,12 @@ On a Unix (including OS X) machine: #. Make a git tag for the release:: - git clone git-fusion@raven.ravenbrook.com:mps-version-$VERSION - cd mps-version-$VERSION + git clone ssh://git@perforce.ravenbrook.com:1622/mps-public + cd mps-public + git checkout -b version/$VERSION origin/version/$VERSION git tag -a release-$RELEASE -F - <<END Memory Pool System Kit release $RELEASE. - See <http://www.ravenbrook.com/project/mps/release/>. + See <https://www.ravenbrook.com/project/mps/release/>. END git push --tags git@github.com:Ravenbrook/mps.git @@ -260,6 +272,7 @@ B. Document History 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. +2018-07-30 GDR_ Git Fusion moved to perforce.ravenbrook.com. ========== ===== ========================================================== .. _RB: mailto:rb@ravenbrook.com diff --git a/mps/procedure/version-create.rst b/mps/procedure/version-create.rst index e92e93535d3..b792d1a2d4a 100644 --- a/mps/procedure/version-create.rst +++ b/mps/procedure/version-create.rst @@ -3,6 +3,7 @@ Memory Pool System Version Create Procedure :author: Richard Kistruck :organization: Ravenbrook Limited :date: 2008-10-29 +:Revision: $Id$ :confidentiality: public :copyright: See `C. Copyright and License`_ :readership: MPS developers @@ -17,7 +18,7 @@ releases 1.105.0 and 1.105.1, this document tells you how to abandon the 1.105 lineage and take a new clone from the master sources to create version 1.106). -Refer to "Product Quality Through Change Management" [RB_1999-05-20] +Refer to "Product Quality Through Change Management" [RB_1999-05-20]_ for background, terminology, rationale, and usage guidance. This tells you what "a version" actually is. @@ -30,7 +31,7 @@ you what "a version" actually is. You might not need to create a new version. An alternative is to create a further "point release" on the existing version. Refer to -[RB_1999-05-20] when deciding. (Summary: if changing the +[RB_1999-05-20]_ when deciding. (Summary: if changing the specification, make a new version; if improving the product against an unchanged specification, make a point release.) @@ -73,15 +74,20 @@ evolution. A version has these parts: ~~~~~~~~~~~~~~~~~~~~~~~~~ #. Are you an authenticated Git Fusion user? If not, follow the - git-fusion_ procedure. + follow the `Git Fusion procedures`_ first. - .. _git-fusion: /procedure/git-fusion + .. _Git Fusion procedures: https://info.ravenbrook.com/procedure/git-fusion #. 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. +#. Does ``code/version.c`` in the master sources have the correct + copyright dates for the ``MPSCopyrightNotice`` string? It should + include the current year. If it is wrong, correct and submit it. + + 3.2. Automated procedure ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -92,27 +98,27 @@ Run the script ``tool/branch``, passing the options: * ``-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 +* ``-d "DESCRIPTION"`` — description of the branch (see below) +* ``-g`` — create a corresponding Git branch using Git Fusion + +The branch description will be published in the version index and +should be a short summary of the improvements detailed in the release +notes. 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:: - tool/branch -p master -v -d "Improved interface to generation chains." -y + tool/branch -v -d "Improved interface to generation chains." -g + +Visually check the output of the script against `3.3. Manual +procedure`_, and when satisfied, repeat the invocation with the ``-y`` +option. 3.3. Manual procedure ~~~~~~~~~~~~~~~~~~~~~ -#. 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:: VERSION=A.BBB @@ -123,45 +129,40 @@ the parent branch. A typical invocation looks like this:: View: //info.ravenbrook.com/project/mps/master/... //info.ravenbrook.com/project/$BRANCH/... END -#. Make sure you have no unsubmitted files:: +#. Create the branch itself by running:: - $ p4 opened - File(s) not opened on this client. - - and then:: - - p4 integrate -b $BRANCH - p4 submit -d "Branching master sources for version $VERSION." + p4 populate -b $BRANCH -d "Branching master sources for version $VERSION." #. Determine the origin of the new version:: p4 changes -m 5 //info.ravenbrook.com/project/mps/master/... - Note the latest change that was in before the integrate. + Note the latest change that was in before the populate. #. Update the `table of versions <https://info.ravenbrook.com/project/mps/version/>`_. -#. Make a client specification that can be used by the `git-fusion robot <https://info.ravenbrook.com/infosys/robots>`_ to sync the version:: +#. Add the version to the “mps” and “mps-public” repos published by + Git Fusion by editing ``//.git-fusion/repos/mps/p4gf_config`` and + ``//.git-fusion/repos/mps-public/p4gf_config`` with entries similar + to existing version branches. - p4 client -i <<END - Client: git-fusion-mps-version-$VERSION - Description: Git-fusion client for syncing MPS version $VERSION - Root: /home/git-fusion/.git-fusion/views/mps-version-$VERSION/p4 - View: //info.ravenbrook.com/project/mps/version/$VERSION/... //git-fusion-mps-version-$VERSION/... - END -#. Add an entry to the `list of repositories to push to GitHub <https://info.ravenbrook.com/infosys/robots/git-fusion/etc/pushes>`_:: +3.4. Post-branch checklist +~~~~~~~~~~~~~~~~~~~~~~~~~~ - 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.git\tversion/$VERSION" >> $PUSHES - p4 submit -d "Arranging for MPS version $VERSION to be pushed to GitHub by Git Fusion" $PUSHES +Ensure that the branch appears correctly at: + +#. the internal index at https://info.ravenbrook.com/project/mps/version + +#. the external index at http://www.ravenbrook.com/project/mps/version + +#. the GitHub mirror at https://github.com/Ravenbrook/mps/branches A. References ------------- -.. [RB_1995-05-20] Richard Brooksby; "Product Quality Through Change +.. [RB_1999-05-20] Richard Brooksby; "Product Quality Through Change Management"; Ravenbrook Limited; 1999-05-20; http://www.ravenbrook.com/doc/1999/05/20/pqtcm/ @@ -179,16 +180,18 @@ B. Document History 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. +2016-04-05 RB_ Bringing up to date in preparation for version 1.115. ========== ===== ======================================================== .. _GDR: mailto:gdr@ravenbrook.com +.. _RB: mailto:rb@ravenbrook.com .. _RHSK: mailto:rhsk@ravenbrook.com C. Copyright and License ------------------------ -Copyright © 2002-2014 Ravenbrook Limited. All rights reserved. +Copyright © 2002-2018 Ravenbrook Limited. All rights reserved. <http://www.ravenbrook.com/>. This is an open source license. Contact Ravenbrook for commercial licensing options. diff --git a/mps/readme.txt b/mps/readme.txt index 2959f6eb7b4..115ec3d01d3 100644 --- a/mps/readme.txt +++ b/mps/readme.txt @@ -53,7 +53,7 @@ using it. The basic case is straightforward on supported platforms (see below):: cd code - cc -O2 -c mps.c Unix / Mac OS X (with Xcode command line) + cc -O2 -c mps.c Unix / macOS (with Xcode command line) cl /O2 /c mps.c Windows (with Microsoft SDK or Visual Studio 2010) This will produce an object file you can link with your project. For @@ -72,14 +72,15 @@ 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 Vista or later, on IA-32 and x86-64, using Microsoft Visual + C/C++; -- Linux 2.4 or later, on IA-32 using GCC and on x86-64 using GCC or +- Linux 2.6 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; +- FreeBSD 7 or later, on IA-32 and x86-64, using GCC or Clang/LLVM; -- OS X 10.4 or later, on IA-32 and x86-64, using Clang/LLVM. +- macOS 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 @@ -132,6 +133,7 @@ Document History brought to you in glorious reStructuredText. 2014-01-13 GDR_ Updated supported platforms. 2014-07-04 GDR_ Link to hotfix for WOW64 bug. +2016-03-24 RB_ Adding support for FreeBSD with Clang/LLVM. ========== ===== ====================================================== .. _GDR: mailto:gdr@ravenbrook.com @@ -143,7 +145,7 @@ Document History Copyright and Licence --------------------- -Copyright (C) 2001-2014 Ravenbrook Limited. All rights reserved. +Copyright (C) 2001-2018 Ravenbrook Limited. All rights reserved. <http://www.ravenbrook.com/>. This is an open source license. Contact Ravenbrook for commercial licensing options. diff --git a/mps/test/README b/mps/test/README index 717f17601d7..5d33ad42bbf 100644 --- a/mps/test/README +++ b/mps/test/README @@ -1,30 +1,57 @@ $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"). +This is the Memory Management QA test harness. To use it you need Perl +5 (or higher). -Testing on Unix ---------------- +Quick start +----------- -From the test directory:: +In a shell in the test directory:: - PLATFORM=lii6ll # substitute your platform - VARIETY=cool # or hot - CODE=../code # code directory of the branch you are testing - make -B -C $CODE -f $PLATFORM.gmk VARIETY=$VARIETY $PLATFORM/$VARIETY/mps.o - alias qa="perl test/qa -i $CODE -l $CODE/$PLATFORM/$VARIETY/mps.o" - qa clib - qa run function/5.c - qa runset testsets/passing + perl test/qa clib + perl test/qa run function/5.c + perl test/qa runset testsets/passing + perl test/qa debug argerr/12.c -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 +Usage and options +----------------- -Or ``gdb`` instead of ``lldb``. MMQA sets its own assertion handler, -so you'll probably want to set a breakpoint on mmqa_assert_handler. +Run ``perl test/qa help`` for help; run ``perl test/qa options`` to +see what version of the harness you have (or look at the file +``test/version``) and which options are available. + +The most important options are the ``-p`` option which specifies the +platform (for example, ``-p lii6ll``) if the auto-detected platform is +not the one you want to test, and the ``-v`` option which specifies +the variety (for example ``-v hot``) if the cool variety is not the +one you want to test. + + +Debugging +--------- + +MMQA sets its own assertion handler, so you'll probably want to set a +breakpoint on ``mmqa_assert_handler``. + + +Windows +------- + +Use a Cygwin shell. Set the ``LANG`` environment variable:: + + export LANG=C + +to avoid locale warnings from Perl. + +The runset command can result in this error:: + + LINK : fatal error LNK1168: cannot open test/obj/nt_AMD64__pc/tmp_test.exe for writing + +You may be able to avoid this by running "View Local Services" from +the Windows Control Panel, double-clicking the "Application +Experience" service, and switching "Startup type" to "Automatic". See +the documentation for LNK1168_. + +.. _LNK1168: https://msdn.microsoft.com/en-us/library/hhbdtt6d.aspx diff --git a/mps/test/argerr/141.c b/mps/test/argerr/141.c index b00f4bc9fbc..a412cb3ec2d 100644 --- a/mps/test/argerr/141.c +++ b/mps/test/argerr/141.c @@ -5,7 +5,7 @@ TEST_HEADER language = c link = testlib.o OUTPUT_SPEC - abort = true + assert_or_abort = true END_HEADER */ diff --git a/mps/test/argerr/146.c b/mps/test/argerr/146.c index c6d465671eb..5cead98d9d8 100644 --- a/mps/test/argerr/146.c +++ b/mps/test/argerr/146.c @@ -5,7 +5,7 @@ TEST_HEADER language = c link = testlib.o OUTPUT_SPEC - abort = true + assert_or_abort = true END_HEADER */ @@ -142,41 +142,7 @@ struct mps_fmt_A_s fmtA = the allocated object to have */ -mycell *allocdumb(mps_ap_t ap, size_t size) -{ - mps_addr_t p; - mycell *q; - size_t bytes; - size_t alignment; - - bytes = offsetof(struct data, ref) + size; - - alignment = MPS_PF_ALIGN; /* needed to make it as wide as size_t */ - -/* twiddle the value of size to make it aligned */ - bytes = (bytes+alignment-1) & ~(alignment-1); - - do - { - die(mps_reserve(&p, ap, bytes), "Reserve: "); - INCCOUNT(RESERVE_COUNT); - q=p; - q->data.tag = MCdata; - q->data.id = nextid; - q->data.copycount = 0; - q->data.numrefs = 0; - q->data.checkedflag = 0; - q->data.size = bytes; - } - while (!mps_commit(ap, p, bytes)); - INCCOUNT(ALLOC_COUNT); - commentif(alloccomments, "allocated id %li at %p.", nextid, q); - nextid += 1; - - return q; -} - -mycell *allocone(mps_ap_t ap, int size) +static mycell *allocone(mps_ap_t ap, int size) { mps_addr_t p; mycell *q; @@ -222,7 +188,6 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) int i; INCCOUNT(SCANCALL_COUNT); - MPS_SCAN_BEGIN(ss) { while (base < limit) { @@ -256,7 +221,9 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) the pun would probably work fine almost everywhere) */ comment("About to fix with null scan state..."); - res = mps_fix(NULL, (mps_addr_t *) &p); + MPS_SCAN_BEGIN(ss) { + res = MPS_FIX12(NULL, (mps_addr_t *) &p); + } MPS_SCAN_END(ss); error("fix with NULL scan state"); if (res != MPS_RES_OK) return res; obj->data.ref[i].addr = p; @@ -273,7 +240,6 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) } } } - MPS_SCAN_END(ss); return MPS_RES_OK; } @@ -390,7 +356,7 @@ static void myfwd(mps_addr_t object, mps_addr_t to) /* set the nth reference of obj to to (n from 0 to size-1) */ -void setref(mycell *obj, int n, mycell *to) +static void setref(mycell *obj, int n, mycell *to) { asserts(obj->tag = MCdata, "setref: from non-data object."); asserts(to->tag = MCdata, "setref: to non-data object."); @@ -400,35 +366,6 @@ void setref(mycell *obj, int n, mycell *to) obj->data.ref[n].id = to->data.id; } -mycell *getref(mycell *obj, int n) -{ - asserts(obj->tag = MCdata, "getref: from non-data object."); - asserts(obj->data.numrefs > n, "getref: access beyond object size."); - return obj->data.ref[n].addr; -} - -mps_addr_t getdata(mycell *obj) -{ - return (mps_addr_t) &(obj->data.ref[0]); -} - -long int getid(mycell *obj) -{ - asserts(obj->tag = MCdata, "getid: non-data object."); - return obj->data.id; -} - -long int getcopycount(mycell *obj) -{ - asserts(obj->tag = MCdata, "getcopycount: non-data object."); - return obj->data.copycount; -} - -long int getsize(mycell *obj) -{ - asserts(obj->tag = MCdata, "getsize: non-data object."); - return obj->data.numrefs; -} /* ---- Now the test itself! ---- */ diff --git a/mps/test/argerr/147.c b/mps/test/argerr/147.c index 0b11507cc81..72e2b34ad69 100644 --- a/mps/test/argerr/147.c +++ b/mps/test/argerr/147.c @@ -5,7 +5,7 @@ TEST_HEADER language = c link = testlib.o OUTPUT_SPEC - abort = true + assert_or_abort = true END_HEADER */ @@ -142,41 +142,8 @@ struct mps_fmt_A_s fmtA = the allocated object to have */ -mycell *allocdumb(mps_ap_t ap, size_t size) -{ - mps_addr_t p; - mycell *q; - size_t bytes; - size_t alignment; - bytes = offsetof(struct data, ref) + size; - - alignment = MPS_PF_ALIGN; /* needed to make it as wide as size_t */ - -/* twiddle the value of size to make it aligned */ - bytes = (bytes+alignment-1) & ~(alignment-1); - - do - { - die(mps_reserve(&p, ap, bytes), "Reserve: "); - INCCOUNT(RESERVE_COUNT); - q=p; - q->data.tag = MCdata; - q->data.id = nextid; - q->data.copycount = 0; - q->data.numrefs = 0; - q->data.checkedflag = 0; - q->data.size = bytes; - } - while (!mps_commit(ap, p, bytes)); - INCCOUNT(ALLOC_COUNT); - commentif(alloccomments, "allocated id %li at %p.", nextid, q); - nextid += 1; - - return q; -} - -mycell *allocone(mps_ap_t ap, int size) +static mycell *allocone(mps_ap_t ap, int size) { mps_addr_t p; mycell *q; @@ -222,7 +189,6 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) int i; INCCOUNT(SCANCALL_COUNT); - MPS_SCAN_BEGIN(ss) { while (base < limit) { @@ -256,7 +222,9 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) the pun would probably work fine almost everywhere) */ comment("About to fix with unaligned scan state..."); - res = mps_fix(UNALIGNED, (mps_addr_t *) &p); + MPS_SCAN_BEGIN(ss) { + res = MPS_FIX12(UNALIGNED, (mps_addr_t *) &p); + } MPS_SCAN_END(ss); error("fix with unaligned scan state"); if (res != MPS_RES_OK) return res; obj->data.ref[i].addr = p; @@ -273,7 +241,6 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) } } } - MPS_SCAN_END(ss); return MPS_RES_OK; } @@ -390,7 +357,7 @@ static void myfwd(mps_addr_t object, mps_addr_t to) /* set the nth reference of obj to to (n from 0 to size-1) */ -void setref(mycell *obj, int n, mycell *to) +static void setref(mycell *obj, int n, mycell *to) { asserts(obj->tag = MCdata, "setref: from non-data object."); asserts(to->tag = MCdata, "setref: to non-data object."); @@ -400,36 +367,6 @@ void setref(mycell *obj, int n, mycell *to) obj->data.ref[n].id = to->data.id; } -mycell *getref(mycell *obj, int n) -{ - asserts(obj->tag = MCdata, "getref: from non-data object."); - asserts(obj->data.numrefs > n, "getref: access beyond object size."); - return obj->data.ref[n].addr; -} - -mps_addr_t getdata(mycell *obj) -{ - return (mps_addr_t) &(obj->data.ref[0]); -} - -long int getid(mycell *obj) -{ - asserts(obj->tag = MCdata, "getid: non-data object."); - return obj->data.id; -} - -long int getcopycount(mycell *obj) -{ - asserts(obj->tag = MCdata, "getcopycount: non-data object."); - return obj->data.copycount; -} - -long int getsize(mycell *obj) -{ - asserts(obj->tag = MCdata, "getsize: non-data object."); - return obj->data.numrefs; -} - /* ---- Now the test itself! ---- */ #define genCOUNT (3) diff --git a/mps/test/argerr/148.c b/mps/test/argerr/148.c index 814429c1cdb..2420e6df343 100644 --- a/mps/test/argerr/148.c +++ b/mps/test/argerr/148.c @@ -5,7 +5,7 @@ TEST_HEADER language = c link = testlib.o OUTPUT_SPEC - abort = true + assert_or_abort = true END_HEADER */ @@ -142,41 +142,7 @@ struct mps_fmt_A_s fmtA = the allocated object to have */ -mycell *allocdumb(mps_ap_t ap, size_t size) -{ - mps_addr_t p; - mycell *q; - size_t bytes; - size_t alignment; - - bytes = offsetof(struct data, ref) + size; - - alignment = MPS_PF_ALIGN; /* needed to make it as wide as size_t */ - -/* twiddle the value of size to make it aligned */ - bytes = (bytes+alignment-1) & ~(alignment-1); - - do - { - die(mps_reserve(&p, ap, bytes), "Reserve: "); - INCCOUNT(RESERVE_COUNT); - q=p; - q->data.tag = MCdata; - q->data.id = nextid; - q->data.copycount = 0; - q->data.numrefs = 0; - q->data.checkedflag = 0; - q->data.size = bytes; - } - while (!mps_commit(ap, p, bytes)); - INCCOUNT(ALLOC_COUNT); - commentif(alloccomments, "allocated id %li at %p.", nextid, q); - nextid += 1; - - return q; -} - -mycell *allocone(mps_ap_t ap, int size) +static mycell *allocone(mps_ap_t ap, int size) { mps_addr_t p; mycell *q; @@ -222,7 +188,6 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) int i; INCCOUNT(SCANCALL_COUNT); - MPS_SCAN_BEGIN(ss) { while (base < limit) { @@ -256,7 +221,10 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) the pun would probably work fine almost everywhere) */ comment("About to fix with null pointer..."); - res = mps_fix(ss, NULL); + MPS_SCAN_BEGIN(ss) { + (void)MPS_FIX1(ss, NULL); + res = MPS_FIX2(ss, NULL); + } MPS_SCAN_END(ss); error("fix with null pointer"); if (res != MPS_RES_OK) return res; obj->data.ref[i].addr = p; @@ -273,7 +241,6 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) } } } - MPS_SCAN_END(ss); return MPS_RES_OK; } @@ -390,7 +357,7 @@ static void myfwd(mps_addr_t object, mps_addr_t to) /* set the nth reference of obj to to (n from 0 to size-1) */ -void setref(mycell *obj, int n, mycell *to) +static void setref(mycell *obj, int n, mycell *to) { asserts(obj->tag = MCdata, "setref: from non-data object."); asserts(to->tag = MCdata, "setref: to non-data object."); @@ -400,36 +367,6 @@ void setref(mycell *obj, int n, mycell *to) obj->data.ref[n].id = to->data.id; } -mycell *getref(mycell *obj, int n) -{ - asserts(obj->tag = MCdata, "getref: from non-data object."); - asserts(obj->data.numrefs > n, "getref: access beyond object size."); - return obj->data.ref[n].addr; -} - -mps_addr_t getdata(mycell *obj) -{ - return (mps_addr_t) &(obj->data.ref[0]); -} - -long int getid(mycell *obj) -{ - asserts(obj->tag = MCdata, "getid: non-data object."); - return obj->data.id; -} - -long int getcopycount(mycell *obj) -{ - asserts(obj->tag = MCdata, "getcopycount: non-data object."); - return obj->data.copycount; -} - -long int getsize(mycell *obj) -{ - asserts(obj->tag = MCdata, "getsize: non-data object."); - return obj->data.numrefs; -} - /* ---- Now the test itself! ---- */ #define genCOUNT (3) diff --git a/mps/test/argerr/149.c b/mps/test/argerr/149.c index c86d9ce3983..0f2fbf58b75 100644 --- a/mps/test/argerr/149.c +++ b/mps/test/argerr/149.c @@ -255,7 +255,7 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) */ comment("About to fix with null pointer..."); p = NULL; - res = mps_fix(ss, (mps_addr_t *) &p); + res = MPS_FIX12(ss, (mps_addr_t *) &p); error("fix with null pointer"); if (res != MPS_RES_OK) return res; obj->data.ref[i].addr = p; diff --git a/mps/test/argerr/150.c b/mps/test/argerr/150.c index 88d751f5d4a..784932ec953 100644 --- a/mps/test/argerr/150.c +++ b/mps/test/argerr/150.c @@ -254,7 +254,7 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) the pun would probably work fine almost everywhere) */ comment("About to fix on unaligned addr..."); - res = mps_fix(ss, UNALIGNED); + res = MPS_FIX12(ss, (mps_addr_t *) UNALIGNED); error("unaligned fix"); if (res != MPS_RES_OK) return res; obj->data.ref[i].addr = p; diff --git a/mps/test/argerr/151.c b/mps/test/argerr/151.c index 19ed2169a93..cbe40d26d5a 100644 --- a/mps/test/argerr/151.c +++ b/mps/test/argerr/151.c @@ -255,7 +255,7 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) */ comment("About to fix with unaligned pointer..."); p = UNALIGNED; - res = mps_fix(ss, &p); + res = MPS_FIX2(ss, &p); error("fix with unaligned pointer"); if (res != MPS_RES_OK) return res; obj->data.ref[i].addr = p; diff --git a/mps/test/argerr/153.c b/mps/test/argerr/153.c index b6d1d6791fa..93048298b11 100644 --- a/mps/test/argerr/153.c +++ b/mps/test/argerr/153.c @@ -1,7 +1,7 @@ /* TEST_HEADER id = $Id$ - summary = very large number as third argument to mps_alloc (MV) + summary = very large number as third argument to mps_alloc (MVFF) language = c link = testlib.o OUTPUT_SPEC @@ -11,7 +11,7 @@ END_HEADER */ #include "testlib.h" -#include "mpscmv.h" +#include "mpscmvff.h" static void test(void) { mps_arena_t arena; @@ -20,10 +20,9 @@ static void test(void) { cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create"); - cdie(mps_pool_create(&pool, arena, mps_class_mv(), - 1024*32, 1024*16, 1024*256), "pool"); + cdie(mps_pool_create_k(&pool, arena, mps_class_mvff(), mps_args_none), "pool"); - cdie(mps_alloc(&q, pool, (size_t) -100 * mmqaArenaSIZE), "alloc"); + cdie(mps_alloc(&q, pool, (size_t)-1 - mmqaArenaSIZE), "alloc"); mps_pool_destroy(pool); mps_arena_destroy(arena); diff --git a/mps/test/argerr/155.c b/mps/test/argerr/155.c new file mode 100644 index 00000000000..9f1228b7750 --- /dev/null +++ b/mps/test/argerr/155.c @@ -0,0 +1,31 @@ +/* +TEST_HEADER + id = $Id: //info.ravenbrook.com/project/mps/master/test/argerr/99.c#4 $ + summary = finalize address not managed by the arena + language = c + link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= global.c + assertcond = PoolOfAddr(&refpool, arena, (Addr)obj) +END_HEADER +*/ + +#include "testlib.h" +#include "mps.h" + +static void test(void) +{ + void *p = &p; + mps_arena_t arena; + cdie(mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none), + "create arena"); + mps_finalize(arena, &p); + mps_arena_destroy(arena); +} + +int main(void) +{ + easy_tramp(test); + return 0; +} diff --git a/mps/test/argerr/156.c b/mps/test/argerr/156.c new file mode 100644 index 00000000000..1b9c0e8aae1 --- /dev/null +++ b/mps/test/argerr/156.c @@ -0,0 +1,31 @@ +/* +TEST_HEADER + id = $Id: //info.ravenbrook.com/project/mps/master/test/argerr/99.c#4 $ + summary = definalize address not managed by the arena + language = c + link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= global.c + assertcond = ArenaHasAddr(arena, (Addr)obj) +END_HEADER +*/ + +#include "testlib.h" +#include "mps.h" + +static void test(void) +{ + void *p = &p; + mps_arena_t arena; + cdie(mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none), + "create arena"); + mps_definalize(arena, &p); + mps_arena_destroy(arena); +} + +int main(void) +{ + easy_tramp(test); + return 0; +} diff --git a/mps/test/argerr/157.c b/mps/test/argerr/157.c new file mode 100644 index 00000000000..d08e079c75e --- /dev/null +++ b/mps/test/argerr/157.c @@ -0,0 +1,37 @@ +/* +TEST_HEADER + id = $Id: //info.ravenbrook.com/project/mps/master/test/argerr/99.c#4 $ + summary = finalize address in manually managed pool + language = c + link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= global.c + assertcond = PoolHasAttr(refpool, AttrGC) +END_HEADER +*/ + +#include "testlib.h" +#include "mps.h" +#include "mpscmvff.h" + +static void test(void) +{ + void *p; + mps_arena_t arena; + mps_pool_t pool; + cdie(mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none), + "create arena"); + cdie(mps_pool_create_k(&pool, arena, mps_class_mvff(), mps_args_none), + "create pool"); + cdie(mps_alloc(&p, pool, 16), "alloc"); + mps_finalize(arena, &p); + mps_pool_destroy(pool); + mps_arena_destroy(arena); +} + +int main(void) +{ + easy_tramp(test); + return 0; +} diff --git a/mps/test/argerr/17.c b/mps/test/argerr/17.c index 6c509d28bd9..6b8da796bff 100644 --- a/mps/test/argerr/17.c +++ b/mps/test/argerr/17.c @@ -5,7 +5,7 @@ TEST_HEADER language = c link = testlib.o newfmt.o OUTPUT_SPEC - abort = true + assert_or_abort = true END_HEADER */ diff --git a/mps/test/argerr/34.c b/mps/test/argerr/34.c index 51c58ee0ab5..4940d182965 100644 --- a/mps/test/argerr/34.c +++ b/mps/test/argerr/34.c @@ -1,7 +1,7 @@ /* TEST_HEADER id = $Id$ - summary = high bit set 3rd arg to mps_alloc (MV) + summary = high bit set 3rd arg to mps_alloc (MVFF) language = c link = testlib.o OUTPUT_SPEC @@ -11,40 +11,27 @@ END_HEADER */ #include "testlib.h" -#include "mpscmv.h" +#include "mpscmvff.h" #include "arg.h" -void *stackpointer; - static void test(void) { mps_arena_t arena; mps_pool_t pool; - mps_thr_t thread; - mps_addr_t a; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); - cdie(mps_thread_reg(&thread, arena), "register thread"); + cdie(mps_pool_create_k(&pool, arena, mps_class_mvff(), mps_args_none), "pool"); - cdie( - mps_pool_create( - &pool, arena, mps_class_mv(), - (size_t) 4096, (size_t) 32, (size_t) 64*1024), - "create pool"); + die(mps_alloc(&a, pool, HIGHBIT_SIZE+8), "allocation failed (correct)"); - die(mps_alloc(&a, pool, HIGHBIT_SIZE+8), - "allocation failed (correct)"); mps_pool_destroy(pool); - + mps_arena_destroy(arena); } int main(void) { - void *m; - stackpointer=&m; /* hack to get stack pointer */ - easy_tramp(test); return 0; } diff --git a/mps/test/argerr/35.c b/mps/test/argerr/35.c index 218721759bf..72ca516e3c4 100644 --- a/mps/test/argerr/35.c +++ b/mps/test/argerr/35.c @@ -1,7 +1,7 @@ /* TEST_HEADER id = $Id$ - summary = unaligned addr_t to free (MV) + summary = unaligned addr_t to free (MVFF) language = c link = testlib.o OUTPUT_SPEC @@ -12,41 +12,28 @@ END_HEADER */ #include "testlib.h" -#include "mpscmv.h" +#include "mpscmvff.h" #include "arg.h" -void *stackpointer; - static void test(void) { mps_arena_t arena; mps_pool_t pool; - mps_thr_t thread; - mps_addr_t a; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); - cdie(mps_thread_reg(&thread, arena), "register thread"); + cdie(mps_pool_create_k(&pool, arena, mps_class_mvff(), mps_args_none), "pool"); - cdie( - mps_pool_create( - &pool, arena, mps_class_mv(), - (size_t) 4096, (size_t) 32, (size_t) 64*1024), - "create pool"); - - die(mps_alloc(&a, pool, 8), - "alloc"); + die(mps_alloc(&a, pool, 8), "alloc"); mps_free(pool, (mps_addr_t) ((char *)a+1), 8); - mps_pool_destroy(pool); + mps_pool_destroy(pool); + mps_arena_destroy(arena); } int main(void) { - void *m; - stackpointer=&m; /* hack to get stack pointer */ - easy_tramp(test); return 0; } diff --git a/mps/test/argerr/36.c b/mps/test/argerr/36.c index ac35d796cac..a45812ecfda 100644 --- a/mps/test/argerr/36.c +++ b/mps/test/argerr/36.c @@ -1,7 +1,7 @@ /* TEST_HEADER id = $Id$ - summary = wrong size_t to free (MV) + summary = wrong size_t to free (MVFF debug) language = c link = testlib.o OUTPUT_SPEC @@ -12,7 +12,7 @@ END_HEADER */ #include "testlib.h" -#include "mpscmv.h" +#include "mpscmvff.h" #include "arg.h" void *stackpointer; @@ -29,7 +29,7 @@ static void test(void) cdie(mps_thread_reg(&thread, arena), "register thread"); - cdie(mps_pool_create_k(&pool, arena, mps_class_mv_debug(), mps_args_none), + cdie(mps_pool_create_k(&pool, arena, mps_class_mvff_debug(), mps_args_none), "create pool"); die(mps_alloc(&a, pool, 8), diff --git a/mps/test/argerr/37.c b/mps/test/argerr/37.c index ffcd86a2650..6617e141744 100644 --- a/mps/test/argerr/37.c +++ b/mps/test/argerr/37.c @@ -1,7 +1,7 @@ /* TEST_HEADER id = $Id$ - summary = wrong size_t to free (MV) + summary = wrong size_t to free (MVFF) language = c link = testlib.o OUTPUT_SPEC @@ -12,41 +12,28 @@ END_HEADER */ #include "testlib.h" -#include "mpscmv.h" +#include "mpscmvff.h" #include "arg.h" -void *stackpointer; - static void test(void) { mps_arena_t arena; mps_pool_t pool; - mps_thr_t thread; - mps_addr_t a; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); - cdie(mps_thread_reg(&thread, arena), "register thread"); + cdie(mps_pool_create_k(&pool, arena, mps_class_mvff(), mps_args_none), "pool"); - cdie( - mps_pool_create( - &pool, arena, mps_class_mv(), - (size_t) 4096, (size_t) 32, (size_t) 64*1024), - "create pool"); - - die(mps_alloc(&a, pool, 8), - "alloc a"); + die(mps_alloc(&a, pool, 8), "alloc a"); mps_free(pool, a, HIGHBIT_SIZE+8); - mps_pool_destroy(pool); + mps_pool_destroy(pool); + mps_arena_destroy(arena); } int main(void) { - void *m; - stackpointer=&m; /* hack to get stack pointer */ - easy_tramp(test); return 0; } diff --git a/mps/test/argerr/41.c b/mps/test/argerr/41.c index 8aa1520aa46..4b76e347e09 100644 --- a/mps/test/argerr/41.c +++ b/mps/test/argerr/41.c @@ -1,47 +1,38 @@ /* TEST_HEADER id = $Id$ - summary = zero extendBy for pool_create (MV) + summary = zero extendBy for pool_create (MVFF) language = c link = testlib.o OUTPUT_SPEC assert = true - assertfile P= poolmv.c + assertfile P= poolmvff.c assertcond = extendBy > 0 END_HEADER */ #include "testlib.h" -#include "mpscmv.h" +#include "mpscmvff.h" #include "arg.h" -void *stackpointer; - static void test(void) { mps_arena_t arena; mps_pool_t pool; - mps_thr_t thread; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); - cdie(mps_thread_reg(&thread, arena), "register thread"); - - cdie( - mps_pool_create( - &pool, arena, mps_class_mv(), - (size_t) 0, (size_t) 32, (size_t) 32), - "create pool"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_EXTEND_BY, 0); + cdie(mps_pool_create_k(&pool, arena, mps_class_mvff(), args), "pool"); + } MPS_ARGS_END(args); mps_pool_destroy(pool); - + mps_arena_destroy(arena); } int main(void) { - void *m; - stackpointer=&m; /* hack to get stack pointer */ - easy_tramp(test); return 0; } diff --git a/mps/test/argerr/42.c b/mps/test/argerr/42.c index cfc5acac794..9d36bebbcb1 100644 --- a/mps/test/argerr/42.c +++ b/mps/test/argerr/42.c @@ -1,47 +1,38 @@ /* TEST_HEADER id = $Id$ - summary = zero avgSize for pool_create (MV) + summary = zero avgSize for pool_create (MVFF) language = c link = testlib.o OUTPUT_SPEC assert = true - assertfile P= poolmv.c + assertfile P= poolmvff.c assertcond = avgSize > 0 END_HEADER */ #include "testlib.h" -#include "mpscmv.h" +#include "mpscmvff.h" #include "arg.h" -void *stackpointer; - static void test(void) { mps_arena_t arena; mps_pool_t pool; - mps_thr_t thread; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); - cdie(mps_thread_reg(&thread, arena), "register thread"); - - cdie( - mps_pool_create( - &pool, arena, mps_class_mv(), - (size_t) 32, (size_t) 0, (size_t) 32), - "create pool"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_MEAN_SIZE, 0); + cdie(mps_pool_create_k(&pool, arena, mps_class_mvff(), args), "pool"); + } MPS_ARGS_END(args); mps_pool_destroy(pool); - + mps_arena_destroy(arena); } int main(void) { - void *m; - stackpointer=&m; /* hack to get stack pointer */ - easy_tramp(test); return 0; } diff --git a/mps/test/argerr/43.c b/mps/test/argerr/43.c deleted file mode 100644 index 7e68227aaa6..00000000000 --- a/mps/test/argerr/43.c +++ /dev/null @@ -1,47 +0,0 @@ -/* -TEST_HEADER - id = $Id$ - 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 -*/ - -#include "testlib.h" -#include "mpscmv.h" -#include "arg.h" - -void *stackpointer; - -static void test(void) -{ - mps_arena_t arena; - mps_pool_t pool; - mps_thr_t thread; - - cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); - - cdie(mps_thread_reg(&thread, arena), "register thread"); - - cdie( - mps_pool_create( - &pool, arena, mps_class_mv(), - (size_t) 32, (size_t) 32, (size_t) 0), - "create pool"); - - mps_pool_destroy(pool); - -} - -int main(void) -{ - void *m; - stackpointer=&m; /* hack to get stack pointer */ - - easy_tramp(test); - return 0; -} diff --git a/mps/test/argerr/44.c b/mps/test/argerr/44.c deleted file mode 100644 index f1c752aada3..00000000000 --- a/mps/test/argerr/44.c +++ /dev/null @@ -1,47 +0,0 @@ -/* -TEST_HEADER - id = $Id$ - 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 -*/ - -#include "testlib.h" -#include "mpscmv.h" -#include "arg.h" - -void *stackpointer; - -static void test(void) -{ - mps_arena_t arena; - mps_pool_t pool; - mps_thr_t thread; - - cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); - - cdie(mps_thread_reg(&thread, arena), "register thread"); - - cdie( - mps_pool_create( - &pool, arena, mps_class_mv(), - (size_t) 33, (size_t) 32, (size_t) 32), - "create pool"); - - mps_pool_destroy(pool); - -} - -int main(void) -{ - void *m; - stackpointer=&m; /* hack to get stack pointer */ - - easy_tramp(test); - return 0; -} diff --git a/mps/test/argerr/75.c b/mps/test/argerr/75.c index dae670ff322..848e0171edf 100644 --- a/mps/test/argerr/75.c +++ b/mps/test/argerr/75.c @@ -5,7 +5,7 @@ TEST_HEADER language = c link = testlib.o OUTPUT_SPEC - abort = true + assert_or_abort = true END_HEADER */ diff --git a/mps/test/conerr/10.c b/mps/test/conerr/10.c index ff28d34649c..14b1c9cd8ed 100644 --- a/mps/test/conerr/10.c +++ b/mps/test/conerr/10.c @@ -12,7 +12,6 @@ END_HEADER */ #include "testlib.h" -#include "mpscmv.h" static void test(void) { diff --git a/mps/test/conerr/11.c b/mps/test/conerr/11.c index f3fe41f91c7..300ae95a388 100644 --- a/mps/test/conerr/11.c +++ b/mps/test/conerr/11.c @@ -12,7 +12,6 @@ END_HEADER */ #include "testlib.h" -#include "mpscmv.h" static void test(void) { diff --git a/mps/test/conerr/12.c b/mps/test/conerr/12.c index a8f932114ab..139abce195b 100644 --- a/mps/test/conerr/12.c +++ b/mps/test/conerr/12.c @@ -4,6 +4,10 @@ TEST_HEADER summary = destroy a format though attached to a pool language = c link = testlib.o +OUTPUT_SPEC + assert = true + assertfile P= format.c + assertcond = format->poolCount == 0 END_HEADER */ diff --git a/mps/test/conerr/13.c b/mps/test/conerr/13.c index b3712a99e44..cab4314a6a3 100644 --- a/mps/test/conerr/13.c +++ b/mps/test/conerr/13.c @@ -12,34 +12,17 @@ END_HEADER */ #include "testlib.h" -#include "mpscmv.h" +#include "mpscmvff.h" static void test(void) { - mps_arena_t arena; + mps_arena_t arena = malloc(4096); mps_pool_t pool; - size_t extendBy; - size_t avgSize; - size_t maxSize; - extendBy = (size_t) 4096; - avgSize = (size_t) 32; - maxSize = (size_t) 65536; - -/* cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); -*/ - arena=malloc(64); - - cdie( - mps_pool_create(&pool, arena, mps_class_mv(), - extendBy, avgSize, maxSize), - "create pool"); + cdie(mps_pool_create_k(&pool, arena, mps_class_mvff(), mps_args_none), "pool"); mps_pool_destroy(pool); - comment("Destroyed pool."); - mps_arena_destroy(arena); - comment("Destroyed arena."); } int main(void) diff --git a/mps/test/conerr/14.c b/mps/test/conerr/14.c index 44cd5ea7f9a..13998e9e3cf 100644 --- a/mps/test/conerr/14.c +++ b/mps/test/conerr/14.c @@ -10,29 +10,18 @@ END_HEADER */ #include "testlib.h" -#include "mpscmv.h" +#include "mpscmvff.h" static void test(void) { mps_arena_t arena; mps_pool_t pool; - size_t extendBy; - size_t avgSize; - size_t maxSize; - - extendBy = (size_t) 4096; - avgSize = (size_t) 32; - maxSize = (size_t) 65536; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); mps_arena_destroy(arena); - comment("Destroyed arena."); - cdie( - mps_pool_create(&pool, arena, mps_class_mv(), - extendBy, avgSize, maxSize), - "create pool"); + cdie(mps_pool_create_k(&pool, arena, mps_class_mvff(), mps_args_none), "pool"); } int main(void) diff --git a/mps/test/conerr/15.c b/mps/test/conerr/15.c index 4bd52651fa3..6e1420dce4d 100644 --- a/mps/test/conerr/15.c +++ b/mps/test/conerr/15.c @@ -5,32 +5,20 @@ TEST_HEADER language = c link = testlib.o OUTPUT_SPEC - abort = true + assert = true + assertfile P= mpsi.c + assertcond = TESTT(Pool, pool) END_HEADER */ +#include <stdlib.h> + #include "testlib.h" -#include "mpscmv.h" static void test(void) { - mps_arena_t arena; - mps_pool_t pool = (mps_pool_t)1; - - 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"); -*/ - + mps_pool_t pool = malloc(4096); mps_pool_destroy(pool); - comment("Destroyed pool."); - - mps_arena_destroy(arena); - comment("Destroyed arena."); } int main(void) diff --git a/mps/test/conerr/16.c b/mps/test/conerr/16.c index 5043f96a55f..75bf14aa21f 100644 --- a/mps/test/conerr/16.c +++ b/mps/test/conerr/16.c @@ -12,35 +12,20 @@ END_HEADER */ #include "testlib.h" -#include "mpscmv.h" +#include "mpscmvff.h" static void test(void) { mps_arena_t arena; mps_pool_t pool; - size_t extendBy; - size_t avgSize; - size_t maxSize; - - 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_k(&pool, arena, mps_class_mvff(), mps_args_none), "pool"); mps_pool_destroy(pool); - comment("Destroyed pool."); - mps_pool_destroy(pool); - comment("Destroyed pool again."); - mps_arena_destroy(arena); - comment("Destroyed arena."); } int main(void) diff --git a/mps/test/conerr/18.c b/mps/test/conerr/18.c index 4d13dede9b9..d6ea9e923b5 100644 --- a/mps/test/conerr/18.c +++ b/mps/test/conerr/18.c @@ -6,8 +6,8 @@ TEST_HEADER link = testlib.o OUTPUT_SPEC assert = true - assertfile P= poollo.c - assertcond = FormatArena(pool->format) == arena + assertfile P= poolabs.c + assertcond = FormatArena(format) == arena END_HEADER */ diff --git a/mps/test/conerr/19.c b/mps/test/conerr/19.c index 430d013c7f9..cbac8d6078f 100644 --- a/mps/test/conerr/19.c +++ b/mps/test/conerr/19.c @@ -5,36 +5,23 @@ TEST_HEADER language = c link = testlib.o OUTPUT_SPEC - abort = true + assert = true + assertfile P= mpsi.c + assertcond = TESTT(Pool, pool) END_HEADER */ +#include <stdlib.h> + #include "testlib.h" -#include "mpscmv.h" +#include "mpscmvff.h" static void test(void) { - mps_arena_t arena; - mps_pool_t pool = (mps_pool_t)1; - + mps_pool_t pool = malloc(4096); mps_addr_t obj; - 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_alloc(&obj, pool, 152), "allocate"); - - mps_pool_destroy(pool); - comment("Destroyed pool"); - - mps_arena_destroy(arena); - comment("Destroyed arena."); } int main(void) diff --git a/mps/test/conerr/2.c b/mps/test/conerr/2.c index 78738d5bc85..ba18c963be0 100644 --- a/mps/test/conerr/2.c +++ b/mps/test/conerr/2.c @@ -17,7 +17,7 @@ static void test(void) { mps_arena_t arena; - arena = malloc(64); + arena = malloc(4096); mps_arena_destroy(arena); comment("Destroy arena."); } diff --git a/mps/test/conerr/20.c b/mps/test/conerr/20.c index dcb65653536..3b216844c39 100644 --- a/mps/test/conerr/20.c +++ b/mps/test/conerr/20.c @@ -12,36 +12,23 @@ END_HEADER */ #include "testlib.h" -#include "mpscmv.h" +#include "mpscmvff.h" static void test(void) { mps_arena_t arena; mps_pool_t pool; - 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"); + cdie(mps_pool_create_k(&pool, arena, mps_class_mvff(), mps_args_none), "pool"); mps_pool_destroy(pool); - comment("Destroyed pool"); cdie(mps_alloc(&obj, pool, 152), "allocate"); mps_arena_destroy(arena); - comment("Destroyed arena."); } int main(void) diff --git a/mps/test/conerr/21.c b/mps/test/conerr/21.c index 9d389fc9522..d00cf5d63af 100644 --- a/mps/test/conerr/21.c +++ b/mps/test/conerr/21.c @@ -12,39 +12,25 @@ END_HEADER */ #include "testlib.h" -#include "mpscmv.h" +#include "mpscmvff.h" static void test(void) { mps_arena_t arena; mps_pool_t pool; - 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"); + cdie(mps_pool_create_k(&pool, arena, mps_class_mvff(), mps_args_none), "pool"); cdie(mps_alloc(&obj, pool, 152), "allocate"); mps_pool_destroy(pool); - comment("Destroyed pool"); mps_free(pool, obj, 512); - comment("Freed."); mps_arena_destroy(arena); - comment("Destroyed arena."); } int main(void) diff --git a/mps/test/conerr/22.c b/mps/test/conerr/22.c index fb020003628..9b13e48f93a 100644 --- a/mps/test/conerr/22.c +++ b/mps/test/conerr/22.c @@ -11,40 +11,25 @@ OUTPUT_SPEC END_HEADER */ +#include <stdlib.h> + #include "testlib.h" -#include "mpscmv.h" +#include "mpscmvff.h" static void test(void) { mps_arena_t arena; mps_pool_t pool; - size_t extendBy; - size_t avgSize; - size_t maxSize; - - mps_addr_t obj = (mps_addr_t)MPS_PF_ALIGN; - - extendBy = (size_t) 4096; - avgSize = (size_t) 32; - maxSize = (size_t) 65536; + mps_addr_t obj = malloc(512); 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_alloc(&obj, pool, 152), "allocate"); -*/ + cdie(mps_pool_create_k(&pool, arena, mps_class_mvff(), mps_args_none), "pool"); + mps_free(pool, obj, 512); - comment("Freed."); mps_pool_destroy(pool); - comment("Destroyed pool"); - mps_arena_destroy(arena); - comment("Destroyed arena."); } int main(void) diff --git a/mps/test/conerr/23.c b/mps/test/conerr/23.c index c6541154196..9196264a242 100644 --- a/mps/test/conerr/23.c +++ b/mps/test/conerr/23.c @@ -1,53 +1,36 @@ /* TEST_HEADER id = $Id$ - summary = free though not allocated + summary = double free language = c link = testlib.o OUTPUT_SPEC assert = true - assertfile P= tract.c - assertcond = found + assertfile P= poolmvff.c + assertcond = res == ResOK END_HEADER */ #include "testlib.h" -#include "mpscmv.h" +#include "mpscmvff.h" static void test(void) { mps_arena_t arena; mps_pool_t pool; - 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"); + cdie(mps_pool_create_k(&pool, arena, mps_class_mvff(), mps_args_none), "pool"); cdie(mps_alloc(&obj, pool, 152), "allocate"); mps_free(pool, obj, 152); - comment("Freed."); - mps_free(pool, obj, 152); - comment("Freed again."); mps_pool_destroy(pool); - comment("Destroyed pool"); - mps_arena_destroy(arena); - comment("Destroyed arena."); } int main(void) diff --git a/mps/test/conerr/25.c b/mps/test/conerr/25.c index 35e4c5c03c7..c4cfc508eea 100644 --- a/mps/test/conerr/25.c +++ b/mps/test/conerr/25.c @@ -30,13 +30,13 @@ static void test(void) cdie(mps_ap_create(&ap, pool), "create ap"); - cdie(mps_reserve(&obj, ap, 152), "reserve"); - (void)mps_commit(ap, &obj, 152); + cdie(mps_reserve(&obj, ap, 256), "reserve"); + (void)mps_commit(ap, &obj, 256); - mps_free(pool, obj, 152); + mps_free(pool, obj, 256); comment("Freed."); - mps_free(pool, obj, 152); + mps_free(pool, obj, 256); comment("Freed again."); mps_pool_destroy(pool); diff --git a/mps/test/conerr/26.c b/mps/test/conerr/26.c index 8fcabc44357..6992cc8f1e0 100644 --- a/mps/test/conerr/26.c +++ b/mps/test/conerr/26.c @@ -12,28 +12,29 @@ END_HEADER */ #include "testlib.h" -#include "mpscmv.h" +#include "mpscmvff.h" static void test(void) { mps_arena_t arena; - mps_pool_t pool0; - mps_pool_t pool1; + mps_pool_t pool0, pool1; mps_addr_t obj; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); - cdie(mps_pool_create_k(&pool0, arena, mps_class_mv(), mps_args_none), + cdie(mps_pool_create_k(&pool0, arena, mps_class_mvff(), mps_args_none), "create pool 0"); - cdie(mps_pool_create_k(&pool1, arena, mps_class_mv(), mps_args_none), + cdie(mps_pool_create_k(&pool1, arena, mps_class_mvff(), mps_args_none), "create pool 1"); cdie(mps_alloc(&obj, pool0, 152), "allocate in 0"); mps_free(pool1, obj, 512); - comment("Freed in 1."); + mps_pool_destroy(pool1); + mps_pool_destroy(pool0); + mps_arena_destroy(arena); } int main(void) diff --git a/mps/test/conerr/27.c b/mps/test/conerr/27.c index 07cbf6b2157..e84ce573c9f 100644 --- a/mps/test/conerr/27.c +++ b/mps/test/conerr/27.c @@ -12,34 +12,28 @@ END_HEADER */ #include "testlib.h" -#include "mpscmv.h" +#include "mpscmvff.h" static void test(void) { mps_arena_t arena; - mps_pool_t pool0; - mps_pool_t pool1; + mps_pool_t pool0, pool1; mps_addr_t obj; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); - cdie(mps_pool_create_k(&pool0, arena, mps_class_mv(), mps_args_none), + cdie(mps_pool_create_k(&pool0, arena, mps_class_mvff(), mps_args_none), "create pool 0"); - cdie(mps_pool_create_k(&pool1, arena, mps_class_mv(), mps_args_none), + cdie(mps_pool_create_k(&pool1, arena, mps_class_mvff(), mps_args_none), "create pool 1"); cdie(mps_alloc(&obj, pool0, 152), "allocate in 0"); mps_pool_destroy(pool1); - comment("Pool 1 destroyed."); - mps_free(pool1, obj, 512); - comment("Freed in 1."); - mps_pool_destroy(pool0); - comment("Pool 0 destroyed."); - + mps_arena_destroy(arena); } int main(void) diff --git a/mps/test/conerr/4.c b/mps/test/conerr/4.c index 38451258bdf..40dc9e59102 100644 --- a/mps/test/conerr/4.c +++ b/mps/test/conerr/4.c @@ -7,33 +7,23 @@ TEST_HEADER OUTPUT_SPEC assert = true assertfile P= global.c - assertcond = RingLength(&arenaGlobals->poolRing) == 5 + assertcond = RingLength(&arenaGlobals->poolRing) == arenaGlobals->systemPools END_HEADER */ #include "testlib.h" -#include "mpscmv.h" +#include "mpscmvff.h" static void test(void) { mps_arena_t arena; mps_pool_t pool; - size_t extendBy; - size_t avgSize; - size_t maxSize; - - 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_k(&pool, arena, mps_class_mvff(), mps_args_none), "pool"); + mps_arena_destroy(arena); - comment("Destroy arena."); } int main(void) diff --git a/mps/test/conerr/44a.c b/mps/test/conerr/44a.c index 6a430ae5e70..96a13d43849 100644 --- a/mps/test/conerr/44a.c +++ b/mps/test/conerr/44a.c @@ -5,7 +5,7 @@ TEST_HEADER language = c link = myfmt.o testlib.o OUTPUT_SPEC - abort = true + assert_or_abort = true END_HEADER */ diff --git a/mps/test/conerr/5.c b/mps/test/conerr/5.c index f6559c4824e..43252ffdfad 100644 --- a/mps/test/conerr/5.c +++ b/mps/test/conerr/5.c @@ -12,7 +12,6 @@ END_HEADER */ #include "testlib.h" -#include "mpscmv.h" static void test(void) { diff --git a/mps/test/conerr/53.c b/mps/test/conerr/53.c index 0cd7a08ac37..ac072dfda0d 100644 --- a/mps/test/conerr/53.c +++ b/mps/test/conerr/53.c @@ -7,10 +7,12 @@ TEST_HEADER OUTPUT_SPEC assert = true assertfile P= ld.c - assertcond = ld->_epoch <= arena->epoch + assertcond = ld->_epoch <= ArenaHistory(arena)->epoch END_HEADER */ +#include <string.h> + #include "testlib.h" #include "mpscamc.h" #include "myfmt.h" @@ -20,6 +22,9 @@ static void test(void) mps_arena_t arena; mps_ld_s ld; + /* overwrite ld with junk */ + memset(&ld, 0xff, sizeof ld); + cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); /* diff --git a/mps/test/conerr/54.c b/mps/test/conerr/54.c index 994a5f73b7d..3439f22cb71 100644 --- a/mps/test/conerr/54.c +++ b/mps/test/conerr/54.c @@ -7,10 +7,12 @@ TEST_HEADER OUTPUT_SPEC assert = true assertfile P= ld.c - assertcond = ld->_epoch <= arena->epoch + assertcond = ld->_epoch <= history->epoch END_HEADER */ +#include <string.h> + #include "testlib.h" #include "mpscamc.h" #include "myfmt.h" @@ -20,6 +22,9 @@ static void test(void) mps_arena_t arena; mps_ld_s ld; + /* overwrite ld with junk */ + memset(&ld, 0xff, sizeof ld); + cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); /* diff --git a/mps/test/conerr/59.c b/mps/test/conerr/59.c index 347780adce1..1c93cd8127c 100644 --- a/mps/test/conerr/59.c +++ b/mps/test/conerr/59.c @@ -1,51 +1,35 @@ /* TEST_HEADER id = $Id$ - summary = free though not allocated + summary = free with wrong size language = c link = testlib.o OUTPUT_SPEC assert = true - assertfile P= poolmv.c - assertcond = unreachable code + assertfile P= poolmvff.c + assertcond = res == ResOK END_HEADER */ #include "testlib.h" -#include "mpscmv.h" +#include "mpscmvff.h" static void test(void) { mps_arena_t arena; mps_pool_t pool; - 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"); - cdie(mps_alloc(&obj, pool, 152), "allocate"); - cdie(mps_alloc(&obj, pool, 4), "alloc2"); + cdie(mps_pool_create_k(&pool, arena, mps_class_mvff(), mps_args_none), "pool"); + cdie(mps_alloc(&obj, pool, 152), "allocate"); mps_free(pool, obj, 512); - comment("Freed."); mps_pool_destroy(pool); - comment("Destroyed pool"); - mps_arena_destroy(arena); - comment("Destroyed arena."); } int main(void) diff --git a/mps/test/conerr/6.c b/mps/test/conerr/6.c index bfc417aa9f2..33d7b5baf7a 100644 --- a/mps/test/conerr/6.c +++ b/mps/test/conerr/6.c @@ -12,7 +12,6 @@ END_HEADER */ #include "testlib.h" -#include "mpscmv.h" void *stackpointer; diff --git a/mps/test/conerr/60.c b/mps/test/conerr/60.c new file mode 100644 index 00000000000..de43e0953f6 --- /dev/null +++ b/mps/test/conerr/60.c @@ -0,0 +1,73 @@ +/* +TEST_HEADER + id = $Id$ + summary = LO pool asserts on unaligned exact reference + language = c + link = myfmt.o testlib.o +OUTPUT_SPEC + assert = true + assertfile P= poollo.c + assertcond = ss->rank == RankAMBIG +END_HEADER +*/ + +#include "testlib.h" +#include "mpscams.h" +#include "mpsclo.h" +#include "myfmt.h" + +static void test(void) +{ + void *marker = ▮ + mps_arena_t arena; + mps_pool_t pool_ams, pool_lo; + mps_thr_t thread; + mps_root_t root; + mps_fmt_t format; + mps_ap_t ap_ams, ap_lo; + mps_addr_t p, q, unaligned; + + cdie(mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none), "create arena"); + mps_arena_park(arena); + cdie(mps_thread_reg(&thread, arena), "register thread"); + cdie(mps_root_create_thread(&root, arena, thread, marker), "create root"); + 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_ams, arena, mps_class_ams(), args), "ams pool"); + } MPS_ARGS_END(args); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_FORMAT, format); + cdie(mps_pool_create_k(&pool_lo, arena, mps_class_lo(), args), "lo pool"); + } MPS_ARGS_END(args); + cdie(mps_ap_create(&ap_ams, pool_ams, mps_rank_exact()), "ams ap"); + cdie(mps_ap_create_k(&ap_lo, pool_lo, mps_args_none), "lo ap"); + + /* p is in the LO pool */ + p = allocone(ap_lo, 0, NULL, NULL, sizeof(mycell)); + + /* q is in the AMS pool with unaligned exact reference to p */ + unaligned = (void *)((char*)p + 1); + q = allocone(ap_ams, 1, p, unaligned, sizeof(mycell)); + + mps_arena_start_collect(arena); + mps_arena_park(arena); + + /* Keep q (and thus p) alive during the collection. */ + report("q", "%p", q); + + mps_ap_destroy(ap_lo); + mps_ap_destroy(ap_ams); + mps_pool_destroy(pool_lo); + mps_pool_destroy(pool_ams); + mps_fmt_destroy(format); + mps_root_destroy(root); + mps_thread_dereg(thread); + mps_arena_destroy(arena); +} + +int main(void) +{ + easy_tramp(test); + return 0; +} diff --git a/mps/test/conerr/61.c b/mps/test/conerr/61.c new file mode 100644 index 00000000000..f17ee06f778 --- /dev/null +++ b/mps/test/conerr/61.c @@ -0,0 +1,65 @@ +/* +TEST_HEADER + id = $Id$ + summary = AWL pool asserts on unaligned exact reference + language = c + link = myfmt.o testlib.o +OUTPUT_SPEC + assert = true + assertfile P= poolawl.c + assertcond = ss->rank == RankAMBIG +END_HEADER +*/ + +#include "testlib.h" +#include "mpscawl.h" +#include "myfmt.h" + +static void test(void) +{ + void *marker = ▮ + mps_arena_t arena; + mps_pool_t pool; + mps_thr_t thread; + mps_root_t root; + mps_fmt_t format; + mps_ap_t ap; + mps_addr_t p, q, unaligned; + + cdie(mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none), "create arena"); + mps_arena_park(arena); + cdie(mps_thread_reg(&thread, arena), "register thread"); + cdie(mps_root_create_thread(&root, arena, thread, marker), "create root"); + 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_awl(), args), "pool"); + } MPS_ARGS_END(args); + cdie(mps_ap_create(&ap, pool, mps_rank_exact()), "ap"); + + /* p is in the AWL pool */ + p = allocone(ap, 0, NULL, NULL, sizeof(mycell)); + + /* q is in the AWL pool with unaligned exact reference to p */ + unaligned = (void *)((char*)p + 1); + q = allocone(ap, 1, p, unaligned, sizeof(mycell)); + + mps_arena_start_collect(arena); + mps_arena_park(arena); + + /* Keep q (and thus p) alive during the collection. */ + report("q", "%p", q); + + mps_ap_destroy(ap); + mps_pool_destroy(pool); + mps_fmt_destroy(format); + mps_root_destroy(root); + mps_thread_dereg(thread); + mps_arena_destroy(arena); +} + +int main(void) +{ + easy_tramp(test); + return 0; +} diff --git a/mps/test/conerr/62.c b/mps/test/conerr/62.c new file mode 100644 index 00000000000..55e86998ad6 --- /dev/null +++ b/mps/test/conerr/62.c @@ -0,0 +1,75 @@ +/* +TEST_HEADER + id = $Id$ + summary = LO pool asserts on exact reference to unallocated object + language = c + link = myfmt.o testlib.o +OUTPUT_SPEC + assert = true + assertfile P= poollo.c + assertcond = ss->rank == RankAMBIG +END_HEADER +*/ + +#include "testlib.h" +#include "mpscams.h" +#include "mpsclo.h" +#include "myfmt.h" + +static void test(void) +{ + void *marker = ▮ + mps_arena_t arena; + mps_pool_t pool_ams, pool_lo; + mps_thr_t thread; + mps_root_t root; + mps_fmt_t format; + mps_ap_t ap_ams, ap_lo; + mps_addr_t p, q, unallocated; + + cdie(mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none), "create arena"); + mps_arena_park(arena); + cdie(mps_thread_reg(&thread, arena), "register thread"); + cdie(mps_root_create_thread(&root, arena, thread, marker), "create root"); + 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_ams, arena, mps_class_ams(), args), "ams pool"); + } MPS_ARGS_END(args); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_FORMAT, format); + cdie(mps_pool_create_k(&pool_lo, arena, mps_class_lo(), args), "lo pool"); + } MPS_ARGS_END(args); + cdie(mps_ap_create(&ap_ams, pool_ams, mps_rank_exact()), "ams ap"); + cdie(mps_ap_create_k(&ap_lo, pool_lo, mps_args_none), "lo ap"); + + /* p is in the LO pool */ + p = allocone(ap_lo, 0, NULL, NULL, sizeof(mycell)); + + /* Destroy the LO allocation point so that p's segment gets unbuffered. */ + mps_ap_destroy(ap_lo); + + /* q is in the AMS pool with exact reference to p and unallocated object */ + unallocated = (void *)((char*)p + sizeof(mycell)); + q = allocone(ap_ams, 1, p, unallocated, sizeof(mycell)); + + mps_arena_start_collect(arena); + mps_arena_park(arena); + + /* Keep q (and thus p) alive during the collection. */ + report("q", "%p", q); + + mps_ap_destroy(ap_ams); + mps_pool_destroy(pool_lo); + mps_pool_destroy(pool_ams); + mps_fmt_destroy(format); + mps_root_destroy(root); + mps_thread_dereg(thread); + mps_arena_destroy(arena); +} + +int main(void) +{ + easy_tramp(test); + return 0; +} diff --git a/mps/test/conerr/63.c b/mps/test/conerr/63.c new file mode 100644 index 00000000000..65ce19823dd --- /dev/null +++ b/mps/test/conerr/63.c @@ -0,0 +1,67 @@ +/* +TEST_HEADER + id = $Id$ + summary = AWL pool asserts on exact reference to unallocated object + language = c + link = myfmt.o testlib.o +OUTPUT_SPEC + assert = true + assertfile P= poolawl.c + assertcond = ss->rank == RankAMBIG +END_HEADER +*/ + +#include "testlib.h" +#include "mpscawl.h" +#include "myfmt.h" + +static void test(void) +{ + void *marker = ▮ + mps_arena_t arena; + mps_pool_t pool; + mps_thr_t thread; + mps_root_t root; + mps_fmt_t format; + mps_ap_t ap; + mps_addr_t p, q, unallocated; + + cdie(mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none), "create arena"); + mps_arena_park(arena); + cdie(mps_thread_reg(&thread, arena), "register thread"); + cdie(mps_root_create_thread(&root, arena, thread, marker), "create root"); + 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_awl(), args), "pool"); + } MPS_ARGS_END(args); + cdie(mps_ap_create(&ap, pool, mps_rank_exact()), "ap"); + + /* p is in the AWL pool */ + p = allocone(ap, 0, NULL, NULL, sizeof(mycell)); + + /* q is in the AWL pool with exact references to p and unallocated object */ + unallocated = (void *)((char*)p + 2 * sizeof(mycell)); + q = allocone(ap, 1, p, unallocated, sizeof(mycell)); + + /* Destroy the allocation point so that the segment gets unbuffered. */ + mps_ap_destroy(ap); + + mps_arena_start_collect(arena); + mps_arena_park(arena); + + /* Keep q (and thus p) alive during the collection. */ + report("q", "%p", q); + + mps_pool_destroy(pool); + mps_fmt_destroy(format); + mps_root_destroy(root); + mps_thread_dereg(thread); + mps_arena_destroy(arena); +} + +int main(void) +{ + easy_tramp(test); + return 0; +} diff --git a/mps/test/conerr/64.c b/mps/test/conerr/64.c new file mode 100644 index 00000000000..1a5ccd43af2 --- /dev/null +++ b/mps/test/conerr/64.c @@ -0,0 +1,80 @@ +/* +TEST_HEADER + id = $Id$ + summary = LO pool asserts on exact out-of-bounds reference + language = c + link = myfmt.o testlib.o +OUTPUT_SPEC + assert = true + assertfile P= poollo.c + assertcond = ss->rank == RankAMBIG +END_HEADER +*/ + +#include "testlib.h" +#include "mpscams.h" +#include "mpsclo.h" +#include "myfmt.h" + +static void test(void) +{ + void *marker = ▮ + mps_arena_t arena; + mps_pool_t pool_ams, pool_lo; + mps_thr_t thread; + mps_root_t root; + mps_fmt_t fmt; + mps_ap_t ap_ams, ap_lo; + mps_addr_t p, q, out_of_bounds; + size_t header = sizeof(mycell); + + cdie(mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none), "create arena"); + mps_arena_park(arena); + cdie(mps_thread_reg(&thread, arena), "register thread"); + cdie(mps_root_create_thread(&root, arena, thread, marker), "create root"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_FMT_HEADER_SIZE, header); + fmtargs(args + 1); + cdie(mps_fmt_create_k(&fmt, arena, args), "lo format"); + } MPS_ARGS_END(args); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_FORMAT, fmt); + cdie(mps_pool_create_k(&pool_ams, arena, mps_class_ams(), args), "ams pool"); + } MPS_ARGS_END(args); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_FORMAT, fmt); + cdie(mps_pool_create_k(&pool_lo, arena, mps_class_lo(), args), "lo pool"); + } MPS_ARGS_END(args); + cdie(mps_ap_create(&ap_ams, pool_ams, mps_rank_exact()), "ams ap"); + cdie(mps_ap_create_k(&ap_lo, pool_lo, mps_args_none), "lo ap"); + + /* p is in the LO pool */ + p = allocheader(ap_lo, 0, NULL, NULL, sizeof(mycell), header); + + /* Destroy the LO allocation point so that p's segment gets unbuffered. */ + mps_ap_destroy(ap_lo); + + /* q is in the AMS pool with exact reference to p and out-of-bounds object */ + out_of_bounds = (void *)((char*)p - header); + q = allocheader(ap_ams, 1, p, out_of_bounds, sizeof(mycell), header); + + mps_arena_start_collect(arena); + mps_arena_park(arena); + + /* Keep q (and thus p) alive during the collection. */ + report("q", "%p", q); + + mps_ap_destroy(ap_ams); + mps_pool_destroy(pool_lo); + mps_pool_destroy(pool_ams); + mps_fmt_destroy(fmt); + mps_root_destroy(root); + mps_thread_dereg(thread); + mps_arena_destroy(arena); +} + +int main(void) +{ + easy_tramp(test); + return 0; +} diff --git a/mps/test/conerr/65.c b/mps/test/conerr/65.c new file mode 100644 index 00000000000..8e31644697e --- /dev/null +++ b/mps/test/conerr/65.c @@ -0,0 +1,72 @@ +/* +TEST_HEADER + id = $Id$ + summary = AWL pool asserts on exact out-of-bounds reference + language = c + link = myfmt.o testlib.o +OUTPUT_SPEC + assert = true + assertfile P= poolawl.c + assertcond = ss->rank == RankAMBIG +END_HEADER +*/ + +#include "testlib.h" +#include "mpscawl.h" +#include "myfmt.h" + +static void test(void) +{ + void *marker = ▮ + mps_arena_t arena; + mps_pool_t pool; + mps_thr_t thread; + mps_root_t root; + mps_fmt_t fmt; + mps_ap_t ap; + mps_addr_t p, q, out_of_bounds; + size_t header = sizeof(mycell); + + cdie(mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none), "create arena"); + mps_arena_park(arena); + cdie(mps_thread_reg(&thread, arena), "register thread"); + cdie(mps_root_create_thread(&root, arena, thread, marker), "create root"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_FMT_HEADER_SIZE, header); + fmtargs(args + 1); + cdie(mps_fmt_create_k(&fmt, arena, args), "format"); + } MPS_ARGS_END(args); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_FORMAT, fmt); + cdie(mps_pool_create_k(&pool, arena, mps_class_awl(), args), "pool"); + } MPS_ARGS_END(args); + cdie(mps_ap_create(&ap, pool, mps_rank_exact()), "ap"); + + /* p is in the AWL pool */ + p = allocheader(ap, 0, NULL, NULL, sizeof(mycell), header); + + /* q is in the AMS pool with exact reference to p and out-of-bounds object */ + out_of_bounds = (void *)((char*)p - header); + q = allocheader(ap, 1, p, out_of_bounds, sizeof(mycell), header); + + /* Destroy the allocation point so that the segment gets unbuffered. */ + mps_ap_destroy(ap); + + mps_arena_start_collect(arena); + mps_arena_park(arena); + + /* Keep q (and thus p) alive during the collection. */ + report("q", "%p", q); + + mps_pool_destroy(pool); + mps_fmt_destroy(fmt); + mps_root_destroy(root); + mps_thread_dereg(thread); + mps_arena_destroy(arena); +} + +int main(void) +{ + easy_tramp(test); + return 0; +} diff --git a/mps/test/conerr/66.c b/mps/test/conerr/66.c new file mode 100644 index 00000000000..2aa1093d106 --- /dev/null +++ b/mps/test/conerr/66.c @@ -0,0 +1,65 @@ +/* +TEST_HEADER + id = $Id$ + summary = AMS pool asserts on unaligned exact reference + language = c + link = myfmt.o testlib.o +OUTPUT_SPEC + assert = true + assertfile P= poolams.c + assertcond = ss->rank == RankAMBIG +END_HEADER +*/ + +#include "testlib.h" +#include "mpscams.h" +#include "myfmt.h" + +static void test(void) +{ + void *marker = ▮ + mps_arena_t arena; + mps_pool_t pool; + mps_thr_t thread; + mps_root_t root; + mps_fmt_t format; + mps_ap_t ap; + mps_addr_t p, q, unaligned; + + cdie(mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none), "create arena"); + mps_arena_park(arena); + cdie(mps_thread_reg(&thread, arena), "register thread"); + cdie(mps_root_create_thread(&root, arena, thread, marker), "create root"); + 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), "pool"); + } MPS_ARGS_END(args); + cdie(mps_ap_create(&ap, pool, mps_rank_exact()), "ap"); + + /* p is in the AMS pool */ + p = allocone(ap, 0, NULL, NULL, sizeof(mycell)); + + /* q is in the AMS pool with unaligned exact reference to p */ + unaligned = (void *)((char*)p + 1); + q = allocone(ap, 1, p, unaligned, sizeof(mycell)); + + mps_arena_start_collect(arena); + mps_arena_park(arena); + + /* Keep q (and thus p) alive during the collection. */ + report("q", "%p", q); + + mps_ap_destroy(ap); + mps_pool_destroy(pool); + mps_fmt_destroy(format); + mps_root_destroy(root); + mps_thread_dereg(thread); + mps_arena_destroy(arena); +} + +int main(void) +{ + easy_tramp(test); + return 0; +} diff --git a/mps/test/conerr/67.c b/mps/test/conerr/67.c new file mode 100644 index 00000000000..3d03f9cb15d --- /dev/null +++ b/mps/test/conerr/67.c @@ -0,0 +1,67 @@ +/* +TEST_HEADER + id = $Id$ + summary = AMS pool asserts on exact reference to unallocated object + language = c + link = myfmt.o testlib.o +OUTPUT_SPEC + assert = true + assertfile P= poolams.c + assertcond = ss->rank == RankAMBIG +END_HEADER +*/ + +#include "testlib.h" +#include "mpscams.h" +#include "myfmt.h" + +static void test(void) +{ + void *marker = ▮ + mps_arena_t arena; + mps_pool_t pool; + mps_thr_t thread; + mps_root_t root; + mps_fmt_t format; + mps_ap_t ap; + mps_addr_t p, q, unallocated; + + cdie(mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none), "create arena"); + mps_arena_park(arena); + cdie(mps_thread_reg(&thread, arena), "register thread"); + cdie(mps_root_create_thread(&root, arena, thread, marker), "create root"); + 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), "pool"); + } MPS_ARGS_END(args); + cdie(mps_ap_create(&ap, pool, mps_rank_exact()), "ap"); + + /* p is in the AMS pool */ + p = allocone(ap, 0, NULL, NULL, sizeof(mycell)); + + /* q is in the AMS pool with exact references to p and unallocated object */ + unallocated = (void *)((char*)p + 2 * sizeof(mycell)); + q = allocone(ap, 1, p, unallocated, sizeof(mycell)); + + /* Destroy the allocation point so that the segment gets unbuffered. */ + mps_ap_destroy(ap); + + mps_arena_start_collect(arena); + mps_arena_park(arena); + + /* Keep q (and thus p) alive during the collection. */ + report("q", "%p", q); + + mps_pool_destroy(pool); + mps_fmt_destroy(format); + mps_root_destroy(root); + mps_thread_dereg(thread); + mps_arena_destroy(arena); +} + +int main(void) +{ + easy_tramp(test); + return 0; +} diff --git a/mps/test/conerr/68.c b/mps/test/conerr/68.c new file mode 100644 index 00000000000..5b2875e5d4e --- /dev/null +++ b/mps/test/conerr/68.c @@ -0,0 +1,72 @@ +/* +TEST_HEADER + id = $Id$ + summary = AMS pool asserts on exact out-of-bounds reference + language = c + link = myfmt.o testlib.o +OUTPUT_SPEC + assert = true + assertfile P= poolams.c + assertcond = ss->rank == RankAMBIG +END_HEADER +*/ + +#include "testlib.h" +#include "mpscams.h" +#include "myfmt.h" + +static void test(void) +{ + void *marker = ▮ + mps_arena_t arena; + mps_pool_t pool; + mps_thr_t thread; + mps_root_t root; + mps_fmt_t fmt; + mps_ap_t ap; + mps_addr_t p, q, out_of_bounds; + size_t header = sizeof(mycell); + + cdie(mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none), "create arena"); + mps_arena_park(arena); + cdie(mps_thread_reg(&thread, arena), "register thread"); + cdie(mps_root_create_thread(&root, arena, thread, marker), "create root"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_FMT_HEADER_SIZE, header); + fmtargs(args + 1); + cdie(mps_fmt_create_k(&fmt, arena, args), "format"); + } MPS_ARGS_END(args); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_FORMAT, fmt); + cdie(mps_pool_create_k(&pool, arena, mps_class_ams(), args), "pool"); + } MPS_ARGS_END(args); + cdie(mps_ap_create(&ap, pool, mps_rank_exact()), "ap"); + + /* p is in the AMS pool */ + p = allocheader(ap, 0, NULL, NULL, sizeof(mycell), header); + + /* q is in the AMS pool with exact reference to p and out-of-bounds object */ + out_of_bounds = (void *)((char*)p - header); + q = allocheader(ap, 1, p, out_of_bounds, sizeof(mycell), header); + + /* Destroy the allocation point so that the segment gets unbuffered. */ + mps_ap_destroy(ap); + + mps_arena_start_collect(arena); + mps_arena_park(arena); + + /* Keep q (and thus p) alive during the collection. */ + report("q", "%p", q); + + mps_pool_destroy(pool); + mps_fmt_destroy(fmt); + mps_root_destroy(root); + mps_thread_dereg(thread); + mps_arena_destroy(arena); +} + +int main(void) +{ + easy_tramp(test); + return 0; +} diff --git a/mps/test/conerr/7.c b/mps/test/conerr/7.c index 7c5bdab31a2..9ead1898641 100644 --- a/mps/test/conerr/7.c +++ b/mps/test/conerr/7.c @@ -12,7 +12,6 @@ END_HEADER */ #include "testlib.h" -#include "mpscmv.h" void *stackpointer; diff --git a/mps/test/conerr/8.c b/mps/test/conerr/8.c index ba37375ea93..f43543cb2cb 100644 --- a/mps/test/conerr/8.c +++ b/mps/test/conerr/8.c @@ -12,14 +12,13 @@ END_HEADER */ #include "testlib.h" -#include "mpscmv.h" static void test(void) { mps_arena_t arena; mps_fmt_t format; - arena=malloc(64); + arena=malloc(4096); cdie(mps_fmt_create_k(&format, arena, mps_args_none), "create format"); diff --git a/mps/test/conerr/9.c b/mps/test/conerr/9.c index 47779320f8f..bbab451086c 100644 --- a/mps/test/conerr/9.c +++ b/mps/test/conerr/9.c @@ -10,7 +10,6 @@ END_HEADER */ #include "testlib.h" -#include "mpscmv.h" static void test(void) { diff --git a/mps/test/function/100.c b/mps/test/function/100.c index edb9c1c0eb6..718bcda36b9 100644 --- a/mps/test/function/100.c +++ b/mps/test/function/100.c @@ -28,7 +28,7 @@ static void dotest(int kind, size_t unitSize, size_t extendBy, mps_pool_t pool; int i, hd; clock_t time0, time1; - int secs; + double secs; asserts(number <= MAXNUMBER, "number too big"); asserts(unitSize >= sizeof(int), "unitSize too small"); @@ -81,9 +81,9 @@ static void dotest(int kind, size_t unitSize, size_t extendBy, mps_pool_destroy(pool); time1=clock(); - secs=(int) 100*(time1-time0)/CLOCKS_PER_SEC; + secs=(time1-time0)/(double)CLOCKS_PER_SEC; - comment("%s test (%x, %x, %i, %i) in %i centisecs", + comment("%s test (%x, %x, %i, %i) in %.2f s", tdesc[kind], (int) extendBy, (int) unitSize, number, iter, secs); } diff --git a/mps/test/function/101.c b/mps/test/function/101.c index 6275259d3c5..dfd16450bd2 100644 --- a/mps/test/function/101.c +++ b/mps/test/function/101.c @@ -1,7 +1,7 @@ /* TEST_HEADER id = $Id$ - summary = MV functional tests allocate and free in manual variable pool + summary = MVFF allocate and free language = c link = testlib.o END_HEADER @@ -9,7 +9,7 @@ END_HEADER #include <time.h> #include "testlib.h" -#include "mpscmv.h" +#include "mpscmvff.h" #define MAXNUMBER 1000000 @@ -54,28 +54,29 @@ static int chkobj(mps_addr_t a, size_t size, unsigned char val) } static void dt(int kind, - size_t extendBy, size_t avgSize, size_t maxSize, + size_t extendBy, size_t avgSize, size_t mins, size_t maxs, int number, int iter) { mps_pool_t pool; int i, hd; clock_t time0, time1; size_t size; - int secs; + double secs; asserts(number <= MAXNUMBER, "number too big"); time0 = clock(); asserts(time0 != -1, "processor time not available"); - die( - mps_pool_create(&pool, arena, mps_class_mv(), - extendBy, avgSize, maxSize), - "create pool"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_EXTEND_BY, extendBy); + MPS_ARGS_ADD(args, MPS_KEY_MEAN_SIZE, avgSize); + cdie(mps_pool_create_k(&pool, arena, mps_class_mvff(), args), "pool"); + } MPS_ARGS_END(args); for(hd=0; hd<number; hd++) { - size = ranrange(mins, maxs); + size = ranrange((unsigned long)mins, (unsigned long)maxs); if ((ranint(2) && (kind & 2)) || (kind==DUMMY)) { queue[hd].addr=NULL; @@ -99,13 +100,13 @@ static void dt(int kind, if (queue[hd].addr != NULL) { asserts(chkobj(queue[hd].addr, queue[hd].size, hd%256), - "corrupt at %x (%s: %x, %x, %x, %x, %x, %i, %i)", + "corrupt at %x (%s: %x, %x, %x, %x, %i, %i)", queue[hd].addr, - tdesc[kind], (int) extendBy, (int) avgSize, (int) maxSize, + tdesc[kind], (int) extendBy, (int) avgSize, (int) mins, (int) maxs, number, iter); mps_free(pool, queue[hd].addr, queue[hd].size); } - size = ranrange(mins, maxs); + size = ranrange((unsigned long)mins, (unsigned long)maxs); if ((ranint(2) && (kind & 2)) || (kind==DUMMY)) { @@ -122,10 +123,10 @@ static void dt(int kind, mps_pool_destroy(pool); time1=clock(); - secs=(int) 100*(time1-time0)/CLOCKS_PER_SEC; + secs=(time1-time0)/(double)CLOCKS_PER_SEC; - comment("%s test (%x, %x, %x, %x, %x, %i, %i) in %i centisecs", - tdesc[kind], (int) extendBy, (int) avgSize, (int) maxSize, + comment("%s test (%x, %x, %x, %x, %i, %i) in %.2f s", + tdesc[kind], (int) extendBy, (int) avgSize, (int) mins, (int) maxs, number, iter, secs); } @@ -139,26 +140,26 @@ static void test(void) mins = sizeof(int); - dt(SEQ, 4096, 32, 64*1024, 8, 9, 5, 10); - dt(RANGAP, 64, 64, 64, 8, 128, 100, 1000); + dt(SEQ, 4096, 32, 8, 9, 5, 10); + dt(RANGAP, 64, 64, 8, 128, 100, 1000); - dt(DUMMY, 4096, 32, 64*1024, 8, 64, 1000, 10000); - dt(SEQ, 4096, 32, 64*1024, 8, 64, 1000, 10000); - dt(RAN, 4096, 32, 64*1024, 8, 64, 1000, 10000); - dt(SEQGAP, 4096, 32, 64*1024, 8, 64, 1000, 10000); - dt(RANGAP, 4096, 32, 64*1024, 8, 64, 1000, 10000); + dt(DUMMY, 4096, 32, 8, 64, 1000, 10000); + dt(SEQ, 4096, 32, 8, 64, 1000, 10000); + dt(RAN, 4096, 32, 8, 64, 1000, 10000); + dt(SEQGAP, 4096, 32, 8, 64, 1000, 10000); + dt(RANGAP, 4096, 32, 8, 64, 1000, 10000); - dt(DUMMY, 4096, 1024, 64*1024, 100, 132, 1000, 10000); - dt(SEQ, 4096, 1024, 64*1024, 100, 132, 1000, 10000); - dt(RAN, 4096, 1024, 64*1024, 100, 132, 1000, 10000); - dt(SEQGAP, 4096, 1024, 64*1024, 100, 132, 1000, 10000); - dt(RANGAP, 4096, 1024, 64*1024, 100, 132, 1000, 10000); + dt(DUMMY, 4096, 1024, 100, 132, 1000, 10000); + dt(SEQ, 4096, 1024, 100, 132, 1000, 10000); + dt(RAN, 4096, 1024, 100, 132, 1000, 10000); + dt(SEQGAP, 4096, 1024, 100, 132, 1000, 10000); + dt(RANGAP, 4096, 1024, 100, 132, 1000, 10000); - dt(DUMMY, 128*1024, 64*1024, 6400*1024, mins, 128*1024, 100, 10000); - dt(SEQ, 128*1024, 64*1024, 6400*1024, mins, 128*1024, 100, 10000); - dt(RAN, 128*1024, 64*1024, 6400*1024, mins, 128*1024, 100, 10000); - dt(SEQGAP, 128*1024, 64*1024, 6400*1024, mins, 128*1024, 100, 10000); - dt(RANGAP, 128*1024, 64*1024, 6400*1024, mins, 128*1024, 100, 10000); + dt(DUMMY, 128*1024, 64*1024, mins, 128*1024, 100, 10000); + dt(SEQ, 128*1024, 64*1024, mins, 128*1024, 100, 10000); + dt(RAN, 128*1024, 64*1024, mins, 128*1024, 100, 10000); + dt(SEQGAP, 128*1024, 64*1024, mins, 128*1024, 100, 10000); + dt(RANGAP, 128*1024, 64*1024, mins, 128*1024, 100, 10000); mps_thread_dereg(thread); mps_arena_destroy(arena); diff --git a/mps/test/function/103.c b/mps/test/function/103.c index 07a77bf2470..58e765e397a 100644 --- a/mps/test/function/103.c +++ b/mps/test/function/103.c @@ -1,7 +1,7 @@ /* TEST_HEADER id = $Id$ - summary = more low memory tests with AMC (using MV) + summary = more low memory tests with AMC (using MVFF) language = c link = testlib.o rankfmt.o END_HEADER @@ -9,7 +9,7 @@ END_HEADER #include "testlib.h" #include "mpscamc.h" -#include "mpscmv.h" +#include "mpscmvff.h" #include "mpsavm.h" #include "rankfmt.h" @@ -22,7 +22,7 @@ static mps_gen_param_s testChain[genCOUNT] = { void *stackpointer; -mps_pool_t poolmv; +mps_pool_t poolmvff; mps_arena_t arena; @@ -32,12 +32,11 @@ static void fillup(void) mps_addr_t a; char *b; - die(mps_pool_create(&poolmv, arena, mps_class_mv(), - (size_t)64, (size_t)64, (size_t)64), - "mps_pool_create"); + cdie(mps_pool_create_k(&poolmvff, arena, mps_class_mvff(), mps_args_none), + "pool create"); size=1024ul*1024ul; while (size) { - while (mps_alloc(&a, poolmv, size)==MPS_RES_OK) { + while (mps_alloc(&a, poolmvff, size)==MPS_RES_OK) { for(b=a; b<(char *)a+size; b++) { *b = 97; } @@ -49,7 +48,7 @@ static void fillup(void) static void empty(void) { - mps_pool_destroy(poolmv); + mps_pool_destroy(poolmvff); } diff --git a/mps/test/function/104.c b/mps/test/function/104.c index 6faba1afab9..f5ac595dcfe 100644 --- a/mps/test/function/104.c +++ b/mps/test/function/104.c @@ -4,6 +4,7 @@ TEST_HEADER summary = test of mps_arena_formatted_objects_walk, inc AMCZ language = c link = testlib.o rankfmt.o + parameters = VERBOSE=0 END_HEADER some kinds of errors that could occur in the walker: @@ -98,7 +99,7 @@ static void stepper(mps_addr_t addr, mps_fmt_t fmt, mps_pool_t pool, appcount += 1; asserts(a->data.checkedflag != newstamp, "III/IV. step on object again at %p", a); - commentif(a->data.checkedflag != oldstamp, + commentif(VERBOSE && a->data.checkedflag != oldstamp, "*. step on unreachable object at %p", a); asserts(oldstamp - a->data.checkedflag < 3, "IV. unreachable object %d stayed alive at %p", a->data.id, a); @@ -169,9 +170,9 @@ static void test(void) for (j=0; j<100; j++) { - comment("%i of 100", j); + comment("%i of 100", j+1); - for (i=0; i<10000; i++) { + for (i=0; i<1000; i++) { k = ranint(4); die(allocrdumb(&a[k], aplo, 64, mps_rank_exact()), "alloc failed"); a[k]->data.checkedflag = newstamp; diff --git a/mps/test/function/106.c b/mps/test/function/106.c index 5f6811bbfaa..7689773b322 100644 --- a/mps/test/function/106.c +++ b/mps/test/function/106.c @@ -4,6 +4,7 @@ TEST_HEADER summary = string twiddling with an AMCZ pool language = c link = lofmt.o testlib.o + parameters = ITERATIONS=10000 END_HEADER */ @@ -14,8 +15,6 @@ END_HEADER #include "mpsavm.h" -#define MAXLEN 1000000; - #define genCOUNT (3) static mps_gen_param_s testChain[genCOUNT] = { @@ -97,7 +96,7 @@ static void test(void) (void)string_ch("Wibble wobble foo"); (void)string_ch("Ba "); - for (i=0; i<10000; i++) { + for (i=0; i<ITERATIONS; i++) { a = conc(string_ch("B"), a); (void)conc(string_ch("Hello there"), string_ch(" folks!")); } diff --git a/mps/test/function/107.c b/mps/test/function/107.c index 9727322b1e6..e493c052c90 100644 --- a/mps/test/function/107.c +++ b/mps/test/function/107.c @@ -4,6 +4,7 @@ TEST_HEADER summary = big allocation with an AMCZ pool language = c link = lofmt.o testlib.o + parameters = ITERATIONS=10000 END_HEADER */ @@ -14,8 +15,6 @@ END_HEADER #include "mpsavm.h" -#define MAXLEN 1000000; - #define genCOUNT (3) static mps_gen_param_s testChain[genCOUNT] = { @@ -97,7 +96,7 @@ static void test(void) (void)string_ch("Wibble wobble foo"); (void)string_ch("Ba "); - for (i=0; i<10000; i++) { + for (i=0; i<ITERATIONS; i++) { a = conc(string_ch("B"), a); (void)conc(string_ch("Hello there"), string_ch(" folks!")); (void)alloclo(ap, 0x4000); diff --git a/mps/test/function/109.c b/mps/test/function/109.c index bccb5cfec7f..b92ad2ff0ef 100644 --- a/mps/test/function/109.c +++ b/mps/test/function/109.c @@ -121,19 +121,19 @@ static void finalpoll(mycell **ref, int faction) static void test(void) { - mps_pool_t poolamc, poolawl, poollo; + mps_pool_t poolamc, poolawl, poolamcz; 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, apawl, apamcz; mycell *a, *b, *c, *d, *z; long int j; - cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t)1024*1024*30), + cdie(mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none), "create arena"); cdie(mps_thread_reg(&thread, arena), "register thread"); @@ -157,7 +157,7 @@ static void test(void) cdie(mps_pool_create(&poolawl, arena, mps_class_awl(), format, getassociated), "create pool(awl)"); - cdie(mmqa_pool_create_chain(&poollo, arena, mps_class_amcz(), format, chain), + cdie(mmqa_pool_create_chain(&poolamcz, arena, mps_class_amcz(), format, chain), "create pool(amcz)"); cdie(mps_ap_create(&apawl, poolawl, mps_rank_weak()), @@ -166,7 +166,7 @@ static void test(void) cdie(mps_ap_create(&apamc, poolamc, mps_rank_exact()), "create ap(amc)"); - cdie(mps_ap_create(&aplo, poollo, mps_rank_exact()), + cdie(mps_ap_create(&apamcz, poolamcz, mps_rank_exact()), "create ap(amcz)"); mps_message_type_enable(arena, mps_message_type_finalization()); @@ -179,7 +179,7 @@ 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! */ + d = allocone(apamcz, 2, mps_rank_exact()); /* rank irrelevant here! */ mps_finalize(arena, (mps_addr_t*)&a); mps_finalize(arena, (mps_addr_t*)&c); mps_finalize(arena, (mps_addr_t*)&d); @@ -211,7 +211,7 @@ static void test(void) /* now to test leaving messages open for a long time! */ for (j=0; j<1000; j++) { - comment("%d of 1000", j); + comment("%d of 1000", j+1); a = allocone(apamc, 10000, mps_rank_exact()); mps_finalize(arena, (mps_addr_t*)&a); final_count +=1; @@ -222,7 +222,7 @@ static void test(void) comment("reregister"); for (j=0; j<500; j++) { - comment("%d of 500", j); + comment("%d of 500", j+1); qpoll(&z, FINAL_REREGISTER); } @@ -230,7 +230,7 @@ static void test(void) z = a; for (j=0; j<1000; j++) { - comment("%d of 1000", j); + comment("%d of 1000", j+1); finalpoll(&z, FINAL_QUEUE); qpoll(&z, FINAL_STORE); a = allocone(apamc, 2, mps_rank_exact()); @@ -270,12 +270,12 @@ static void test(void) mps_arena_park(arena); mps_ap_destroy(apawl); mps_ap_destroy(apamc); - mps_ap_destroy(aplo); + mps_ap_destroy(apamcz); comment("Destroyed aps."); mps_pool_destroy(poolamc); mps_pool_destroy(poolawl); - mps_pool_destroy(poollo); + mps_pool_destroy(poolamcz); comment("Destroyed pools."); mps_chain_destroy(chain); diff --git a/mps/test/function/113.c b/mps/test/function/113.c index a897c7fb60a..107ec68578a 100644 --- a/mps/test/function/113.c +++ b/mps/test/function/113.c @@ -4,6 +4,7 @@ TEST_HEADER summary = AWL and AWL performance language = c link = testlib.o fastfmt.o + parameters = ITERATIONS=10 END_HEADER */ @@ -73,9 +74,9 @@ static void test(void) b = allocone(apamc, 1, mps_rank_exact()); - for (j=1; j<=10; j++) + for (j=1; j<=ITERATIONS; j++) { - comment("%i of 10.", j); + comment("%i of %i.", j, ITERATIONS); a = allocone(apamc, 5, mps_rank_exact()); b = a; c = a; @@ -84,7 +85,7 @@ static void test(void) f = a; g = a; - for (i=1; i<5000; i++) + for (i=1; i<=1000; i++) { c = allocone(apamc, 20, mps_rank_exact()); d = allocone(apawl, 20, mps_rank_exact()); diff --git a/mps/test/function/114.c b/mps/test/function/114.c index c273f30cd52..f2f1b022bbf 100644 --- a/mps/test/function/114.c +++ b/mps/test/function/114.c @@ -84,7 +84,7 @@ static void test(void) f = a; g = a; - for (i=1; i<5000; i++) + for (i=0; i<1000; i++) { c = allocone(apamc, 20, mps_rank_exact()); d = allocone(apawl, 20, mps_rank_exact()); diff --git a/mps/test/function/12.c b/mps/test/function/12.c index b1f8ad9f17c..d9f2de7fedb 100644 --- a/mps/test/function/12.c +++ b/mps/test/function/12.c @@ -4,12 +4,13 @@ TEST_HEADER summary = lots of APs with interleaved reserve and 2-stage commit language = c link = testlib.o newfmt.o + parameters = VERBOSE=0 NCELLS=100 NAPS=100 ITERATIONS=50 END_HEADER */ /* This test needs some explanation. The object 'cells' contains - NCELL references to other objects. NAPS allocation points + NCELLS references to other objects. NAPS allocation points are created. At each step, we choose one and nudge it on a little. Each allocation point goes through the following cycle of steps: reserve, init, begin commit, end commit. @@ -30,11 +31,8 @@ END_HEADER #include "newfmt.h" -#define NCELLS 100 -#define NAPS 100 #define PNULL (ranint(100)<25) #define NUMREFS (ranint(20)) -#define BLAH 0 #define genCOUNT (3) static mps_gen_param_s testChain[genCOUNT] = { @@ -72,8 +70,8 @@ static void test(void) int nextid = 0x1000000; /* turn on comments about copying and scanning */ - formatcomments = BLAH; - fixcomments = BLAH; + formatcomments = VERBOSE; + fixcomments = VERBOSE; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); @@ -105,8 +103,8 @@ static void test(void) 0 after commit */ - for(h=0; h<100; h++) { - comment("%i of 100", h); + for(h=0; h<ITERATIONS; h++) { + comment("%i of %i", h, ITERATIONS); for(j=0; j<1000; j++) { if (j == 500) { @@ -126,11 +124,11 @@ static void test(void) p[i]->data.tag = 0xD033E2A6; p[i]->data.id = nextid; ap_state[i] = 1; - commentif(BLAH, "%i: reserve %li at %p", i, nextid, q); + commentif(VERBOSE, "%i: reserve %li at %p", i, nextid, q); nextid +=1; break; case 1: - commentif(BLAH, "%i: init %li", i, p[i]->data.id); + commentif(VERBOSE, "%i: init %li", i, p[i]->data.id); p[i]->data.tag = MCdata; p[i]->data.numrefs = nrefs[i]; p[i]->data.size = s[i]; @@ -145,22 +143,22 @@ static void test(void) p[i]->data.ref[k].addr = pobj; p[i]->data.ref[k].id = (pobj==NULL ? 0 : pobj->data.id); } - commentif(BLAH, " ref %i -> %li", k, p[i]->data.ref[k].id); + commentif(VERBOSE, " ref %i -> %li", k, p[i]->data.ref[k].id); } break; case 2: - commentif(BLAH, "%i: begin commit %li", i, p[i]->data.id); + commentif(VERBOSE, "%i: begin commit %li", i, p[i]->data.id); ambig[i] = p[i]; ap[i]->init = ap[i]->alloc; ap_state[i] = 3; break; case 3: - commentif(BLAH, "%i: end commit %li", i, p[i]->data.id); + commentif(VERBOSE, "%i: end commit %li", i, p[i]->data.id); q = p[i]; if (ap[i]->limit != 0 || mps_ap_trip(ap[i], p[i], s[i])) { l = ranint(NCELLS); setref(cells, l, q); - commentif(BLAH, "%i -> %i", i, l); + commentif(VERBOSE, "%i -> %i", i, l); } ap_state[i] = 0; ambig[i] = NULL; @@ -176,15 +174,15 @@ static void test(void) for (i=0; i<NAPS; i++) { switch (ap_state[i]) { case 1: - commentif(BLAH, "%i init", i); + commentif(VERBOSE, "%i init", i); p[i]->data.tag = MCdata; p[i]->data.numrefs = 0; p[i]->data.size = s[i]; case 2: - commentif(BLAH, "%i begin commit", i); + commentif(VERBOSE, "%i begin commit", i); ap[i]->init = ap[i]->alloc; case 3: - commentif(BLAH, "% end commit", i); + commentif(VERBOSE, "%i end commit", i); (void) (ap[i]->limit != 0 || mps_ap_trip(ap[i], p[i], s[i])); } mps_ap_destroy(ap[i]); diff --git a/mps/test/function/120.c b/mps/test/function/120.c index 032fa722c3f..cc45b20aa40 100644 --- a/mps/test/function/120.c +++ b/mps/test/function/120.c @@ -21,7 +21,7 @@ END_HEADER #include "testlib.h" #include "mpsavm.h" -#include "mpscmv.h" +#include "mpscmvff.h" void *stackpointer; @@ -34,7 +34,6 @@ static void test(void) { int i; mps_addr_t a; mps_res_t res; - size_t committed; /* Create an arena with a commit limit that's too small for the * essential MPS internal data structures -- this must fail with @@ -60,7 +59,7 @@ static void test(void) { /* create a pool */ - cdie(mps_pool_create(&pool, arena, mps_class_mv(), (size_t) 64, (size_t) 64, (size_t) 64), "pool create"); + cdie(mps_pool_create_k(&pool, arena, mps_class_mvff(), mps_args_none), "pool"); for (i=0; i<100; i++) { die(mps_alloc(&a, pool, (size_t) 64), "alloc"); @@ -110,7 +109,7 @@ static void test(void) { i = 0; while (i < sizeof pools / sizeof pools[0]) { - res = mps_pool_create(&pools[i], arena, mps_class_mv(), (size_t) 64, (size_t) 64, (size_t) 64); + res = mps_pool_create_k(&pools[i], arena, mps_class_mvff(), mps_args_none); if (res == MPS_RES_OK) { i++; } else { diff --git a/mps/test/function/123.c b/mps/test/function/123.c index 4166f30f685..0e354e29bed 100644 --- a/mps/test/function/123.c +++ b/mps/test/function/123.c @@ -35,7 +35,7 @@ static void test(void) mps_fmt_t format; mps_ap_t apamc, apawl; - unsigned int i, c; + mps_word_t i, c; cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) (60ul*1024*1024)), "create arena"); diff --git a/mps/test/function/124.c b/mps/test/function/124.c index cb4620b49de..dae58c55624 100644 --- a/mps/test/function/124.c +++ b/mps/test/function/124.c @@ -4,6 +4,7 @@ TEST_HEADER summary = test of ramp allocation language = c link = testlib.o rankfmt.o + parameters = ITERATIONS=50000 OUTPUT_SPEC result = pass END_HEADER @@ -22,16 +23,14 @@ static mps_gen_param_s testChain[genCOUNT] = { #define ARENALIMIT (200) -#define TABSIZE (50000) -#define ENTERRAMP (30000) -#define LEAVERAMP (100000) +#define TABSIZE (ITERATIONS / 2) +#define ENTERRAMP (ITERATIONS / 10) +#define LEAVERAMP (ITERATIONS / 10) #define BACKSIZE (128) #define BACKITER (32) #define RAMPSIZE (128) -#define ITERATIONS (100000ul) - #define RAMP_INTERFACE /* #define COLLECT_WORLD @@ -99,7 +98,7 @@ static void test(void) /* the compiler doesn't know this. */ for (i = 0; i < ITERATIONS; i++) { - if (i % 10000 == 0) { + if (i * 10 % ITERATIONS == 0) { comment("%ld of %ld", i, ITERATIONS); } alloc_back(); diff --git a/mps/test/function/125.c b/mps/test/function/125.c index 1ca2f61fe4a..52f06f91ec9 100644 --- a/mps/test/function/125.c +++ b/mps/test/function/125.c @@ -66,9 +66,12 @@ static void test(void) /* allocate lots of little objects */ - while (mps_arena_committed(arena) < 1024ul*1024ul*20) { + for (;;) { comment("reserved %ld, committed %ld", mps_arena_reserved(arena), mps_arena_committed(arena)); + if (mps_arena_committed(arena) > 1024ul*1024ul*10) { + break; + } for (j=0; j<10000; j++) { a = allocone(ap, 2, mps_rank_exact()); setref(a, 0, b); @@ -77,6 +80,7 @@ static void test(void) setref(b, 1, a); } mps_arena_collect(arena); + mps_arena_release(arena); } mps_arena_park(arena); diff --git a/mps/test/function/127.c b/mps/test/function/127.c index 55c67961987..8f089c6f320 100644 --- a/mps/test/function/127.c +++ b/mps/test/function/127.c @@ -4,6 +4,7 @@ TEST_HEADER summary = test of ramp allocation -- with collect world instead of ramps language = c link = testlib.o rankfmt.o + parameters = ITERATIONS=50000 OUTPUT_SPEC result = pass END_HEADER @@ -16,16 +17,14 @@ END_HEADER #define ARENALIMIT (200) -#define TABSIZE (50000) -#define ENTERRAMP (30000) -#define LEAVERAMP (100000) +#define TABSIZE (ITERATIONS / 2) +#define ENTERRAMP (ITERATIONS / 10) +#define LEAVERAMP (ITERATIONS / 10) #define BACKSIZE (128) #define BACKITER (32) #define RAMPSIZE (128) -#define ITERATIONS (100000ul) - /* #define RAMP_INTERFACE */ @@ -61,11 +60,11 @@ static void alloc_back(void) { static void test(void) { long int i; - long int rsize; + long int rsize = 0; int inramp; - mycell *r, *s; + mycell *r = NULL, *s; cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) 1024*1024*ARENALIMIT), @@ -99,8 +98,8 @@ static void test(void) { inramp = 0; for (i = 0; i < ITERATIONS; i++) { - if (i % 10000 == 0) { - comment("%ld of %ld", i, ITERATIONS); + if (i * 10 % ITERATIONS == 0) { + comment("%ld of %ld", i+1, ITERATIONS); } alloc_back(); if (inramp) { diff --git a/mps/test/function/128.c b/mps/test/function/128.c index 6414e41d976..cbf04acb0d7 100644 --- a/mps/test/function/128.c +++ b/mps/test/function/128.c @@ -4,6 +4,7 @@ TEST_HEADER summary = test of ramp allocation -- no collect world or ramps language = c link = testlib.o rankfmt.o + parameters = ITERATIONS=50000 OUTPUT_SPEC result = pass END_HEADER @@ -16,16 +17,14 @@ END_HEADER #define ARENALIMIT (200) -#define TABSIZE (50000) -#define ENTERRAMP (30000) -#define LEAVERAMP (100000) +#define TABSIZE (ITERATIONS / 2) +#define ENTERRAMP (ITERATIONS / 10) +#define LEAVERAMP (ITERATIONS / 10) #define BACKSIZE (128) #define BACKITER (32) #define RAMPSIZE (128) -#define ITERATIONS (100000ul) - /* #define RAMP_INTERFACE #define COLLECT_WORLD @@ -61,11 +60,11 @@ static void alloc_back(void) { static void test(void) { long int i; - long int rsize; + long int rsize = 0; int inramp; - mycell *r, *s; + mycell *r = NULL, *s; cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) 1024*1024*ARENALIMIT), @@ -99,7 +98,7 @@ static void test(void) { inramp = 0; for (i = 0; i < ITERATIONS; i++) { - if (i % 10000 == 0) { + if (i * 10 % ITERATIONS == 0) { comment("%ld of %ld", i, ITERATIONS); } alloc_back(); diff --git a/mps/test/function/129.c b/mps/test/function/129.c index 94512cf6e16..eb94f3f7e20 100644 --- a/mps/test/function/129.c +++ b/mps/test/function/129.c @@ -4,6 +4,7 @@ TEST_HEADER summary = test of ramp allocation with small arena language = c link = testlib.o rankfmt.o + parameters = ITERATIONS=10000 OUTPUT_SPEC result = pass END_HEADER @@ -16,16 +17,14 @@ END_HEADER #define ARENALIMIT (2) -#define TABSIZE (50000) -#define ENTERRAMP (30000) -#define LEAVERAMP (100000) +#define TABSIZE (ITERATIONS * 5 / 10) +#define ENTERRAMP (ITERATIONS / 10) +#define LEAVERAMP (ITERATIONS / 10) #define BACKSIZE (128) #define BACKITER (32) #define RAMPSIZE (128) -#define ITERATIONS (100000ul) - #define RAMP_INTERFACE /* #define COLLECT_WORLD @@ -43,7 +42,7 @@ mps_pool_t poolamc; mps_thr_t thread; mps_root_t root, root1; - mps_chain_t chain; +mps_chain_t chain; mps_fmt_t format; mps_ap_t apamc; @@ -98,7 +97,7 @@ static void test(void) { inramp = 0; for (i = 0; i < ITERATIONS; i++) { - if (i % 10000 == 0) { + if (i * 10 % ITERATIONS == 0) { comment("%ld of %ld", i, ITERATIONS); } alloc_back(); diff --git a/mps/test/function/12p.c b/mps/test/function/12p.c index db389e7f14f..e5556c75ecb 100644 --- a/mps/test/function/12p.c +++ b/mps/test/function/12p.c @@ -4,12 +4,13 @@ TEST_HEADER summary = lots of APs with interleaved reserve and 2-stage commit language = c link = testlib.o newfmt.o + parameters = VERBOSE=0 NCELLS=100 NAPS=100 ITERATIONS=100 END_HEADER */ /* This test needs some explanation. The object 'cells' contains - NCELL references to other objects. NAPS allocation points + NCELLS references to other objects. NAPS allocation points are created. At each step, we choose one and nudge it on a little. Each allocation point goes through the following cycle of steps: reserve, init, begin commit, end commit. @@ -35,11 +36,8 @@ static mps_gen_param_s testChain[genCOUNT] = { void *stackpointer; -#define NCELLS 100 -#define NAPS 100 #define PNULL (ranint(100)<25) #define NUMREFS (ranint(20)) -#define BLAH 0 mps_ap_t ap[NAPS]; size_t s[NAPS]; @@ -66,8 +64,8 @@ static void test(void) int nextid = 0x1000000; /* turn on comments about copying and scanning */ - formatcomments = BLAH; - fixcomments = BLAH; + formatcomments = VERBOSE; + fixcomments = VERBOSE; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); @@ -104,9 +102,9 @@ cells = allocone(ap[0], NCELLS); 0 after commit */ - for(h=0; h<100; h++) + for(h=0; h<ITERATIONS; h++) { - comment("%i of 100", h); + comment("%i of %i", h, ITERATIONS); comment("%i collections", (int) mps_collections(arena)); for(j=0; j<1000; j++) @@ -126,11 +124,11 @@ cells = allocone(ap[0], NCELLS); p[i]->data.tag = 0xD033E2A6; p[i]->data.id = nextid; ap_state[i] = 1; - commentif(BLAH, "%i: reserve %li at %p", i, nextid, q); + commentif(VERBOSE, "%i: reserve %li at %p", i, nextid, q); nextid +=1; break; case 1: - commentif(BLAH, "%i: init %li", i, p[i]->data.id); + commentif(VERBOSE, "%i: init %li", i, p[i]->data.id); p[i]->data.tag = MCdata; p[i]->data.numrefs = nrefs[i]; p[i]->data.size = s[i]; @@ -148,11 +146,11 @@ cells = allocone(ap[0], NCELLS); p[i]->data.ref[k].addr = pobj; p[i]->data.ref[k].id = (pobj==NULL ? 0 : pobj->data.id); } - commentif(BLAH, " ref %i -> %li", k, p[i]->data.ref[k].id); + commentif(VERBOSE, " ref %i -> %li", k, p[i]->data.ref[k].id); } break; case 2: - commentif(BLAH, "%i: begin commit %li", i, p[i]->data.id); + commentif(VERBOSE, "%i: begin commit %li", i, p[i]->data.id); ap[i]->init = ap[i]->alloc; ap_state[i] = 3; break; @@ -160,13 +158,13 @@ cells = allocone(ap[0], NCELLS); ap_state[i]+=1; break; case 10: - commentif(BLAH, "%i: end commit %li", i, p[i]->data.id); + commentif(VERBOSE, "%i: end commit %li", i, p[i]->data.id); q=p[i]; if (ap[i]->limit != 0 || mps_ap_trip(ap[i], p[i], s[i])) { l = ranint(NCELLS); setref(cells, l, q); - commentif(BLAH, "%i -> %i", i, l); + commentif(VERBOSE, "%i -> %i", i, l); } ap_state[i] = 0; break; @@ -182,15 +180,15 @@ cells = allocone(ap[0], NCELLS); switch (ap_state[i]) { case 1: - commentif(BLAH, "%i init", i); + commentif(VERBOSE, "%i init", i); p[i]->data.tag = MCdata; p[i]->data.numrefs = 0; p[i]->data.size = s[i]; case 2: - commentif(BLAH, "%i begin commit", i); + commentif(VERBOSE, "%i begin commit", i); ap[i]->init = ap[i]->alloc; case 3: - commentif(BLAH, "% end commit", i); + commentif(VERBOSE, "%i end commit", i); (void) (ap[i]->limit != 0 || mps_ap_trip(ap[i], p[i], s[i])); } mps_ap_destroy(ap[i]); diff --git a/mps/test/function/13.c b/mps/test/function/13.c index aa5ca31b3ee..9a629ea1792 100644 --- a/mps/test/function/13.c +++ b/mps/test/function/13.c @@ -4,12 +4,13 @@ TEST_HEADER summary = interleaved APs (like 12, but produce comments for debugging) language = c link = testlib.o newfmt.o + parameters = VERBOSE=0 NCELLS=100 NAPS=100 ITERATIONS=100 END_HEADER */ /* This test needs some explanation. The object 'cells' contains - NCELL references to other objects. NAPS allocation points + NCELLS references to other objects. NAPS allocation points are created. At each step, we choose one and nudge it on a little. Each allocation point goes through the following cycle of steps: reserve, init, begin commit, end commit. @@ -35,11 +36,8 @@ static mps_gen_param_s testChain[genCOUNT] = { void *stackpointer; -#define NCELLS 100 -#define NAPS 100 #define PNULL (ranint(100)<25) #define NUMREFS (ranint(20)) -#define BLAH 1 mps_ap_t ap[NAPS]; mycell *p[NAPS]; @@ -66,8 +64,8 @@ static void test(void) int nextid = 0x1000000; /* turn on comments about copying and scanning */ - formatcomments = BLAH; - fixcomments = BLAH; + formatcomments = VERBOSE; + fixcomments = VERBOSE; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); @@ -104,9 +102,9 @@ cells = allocone(ap[0], NCELLS); 0 after commit */ - for(h=0; h<100; h++) + for(h=0; h<ITERATIONS; h++) { - comment("%i of 100", h); + comment("%i of %i", h, ITERATIONS); for(j=0; j<1000; j++) { @@ -125,11 +123,11 @@ cells = allocone(ap[0], NCELLS); p[i]->data.tag = 0xD033E2A6; p[i]->data.id = nextid; ap_state[i] = 1; - commentif(BLAH, "%i: reserve %li at %p", i, nextid, q); + commentif(VERBOSE, "%i: reserve %li at %p", i, nextid, q); nextid +=1; break; case 1: - commentif(BLAH, "%i: init %li", i, p[i]->data.id); + commentif(VERBOSE, "%i: init %li", i, p[i]->data.id); p[i]->data.tag = MCdata; p[i]->data.numrefs = nrefs[i]; p[i]->data.size = s[i]; @@ -147,22 +145,22 @@ cells = allocone(ap[0], NCELLS); p[i]->data.ref[k].addr = pobj; p[i]->data.ref[k].id = (pobj==NULL ? 0 : pobj->data.id); } - commentif(BLAH, " ref %i -> %li", k, p[i]->data.ref[k].id); + commentif(VERBOSE, " ref %i -> %li", k, p[i]->data.ref[k].id); } break; case 2: - commentif(BLAH, "%i: begin commit %li", i, p[i]->data.id); + commentif(VERBOSE, "%i: begin commit %li", i, p[i]->data.id); ap[i]->init = ap[i]->alloc; ap_state[i] = 3; break; case 3: - commentif(BLAH, "%i: end commit %li", i, p[i]->data.id); + commentif(VERBOSE, "%i: end commit %li", i, p[i]->data.id); q=p[i]; if (ap[i]->limit != 0 || mps_ap_trip(ap[i], p[i], s[i])) { l = ranint(NCELLS); setref(cells, l, q); - commentif(BLAH, "succeeded %i -> %i", i, l); + commentif(VERBOSE, "succeeded %i -> %i", i, l); } ap_state[i] = 0; break; @@ -171,22 +169,22 @@ cells = allocone(ap[0], NCELLS); checkfrom(cells); } - commentif(BLAH, "Finished main loop"); + commentif(VERBOSE, "Finished main loop"); for (i=0; i<NAPS; i++) { switch (ap_state[i]) { case 1: - commentif(BLAH, "%i init", i); + commentif(VERBOSE, "%i init", i); p[i]->data.tag = MCdata; p[i]->data.numrefs = 0; p[i]->data.size = s[i]; case 2: - commentif(BLAH, "%i begin commit", i); + commentif(VERBOSE, "%i begin commit", i); ap[i]->init = ap[i]->alloc; case 3: - commentif(BLAH, "% end commit", i); + commentif(VERBOSE, "%i end commit", i); (void) (ap[i]->limit != 0 || mps_ap_trip(ap[i], p[i], s[i])); } mps_ap_destroy(ap[i]); diff --git a/mps/test/function/132.c b/mps/test/function/132.c deleted file mode 100644 index b69e6cbcecf..00000000000 --- a/mps/test/function/132.c +++ /dev/null @@ -1,189 +0,0 @@ -/* -TEST_HEADER - id = $Id$ - summary = low-memory reservoir tests with commit limit, part I - language = c - harness = 2.1 - link = testlib.o rankfmt.o -OUTPUT_SPEC - lim0 = 0 - avail0 = 0 - lim1 > 5000000 - lim1 < 6000000 - deficit1 = 0 - lim2 > 1045 - lim2 < 32768 - deficit2 = 0 - deficit3 > 8000000 - spill3 <= 0 - spill4 <= 0 - grow4 > 500000 - allocfail < 20 - failres = COMMIT_LIMIT - spill5 <= 0 - grow5 = 0 - avail5 > 1500000 - allocfail2 > 5000 - failres2 = COMMIT_LIMIT - shrink6 > 1000000 - spill6 <= 0 - completed = yes -END_HEADER -*/ - -#include "testlib.h" -#include "mpscamc.h" -#include "mpsavm.h" -#include "rankfmt.h" - - -#define ARENA_SIZE ((size_t) 1024*1024*30) - -#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) -{ - mps_arena_t arena; - mps_pool_t poolamc; - mps_thr_t thread; - mps_root_t root; - - mps_fmt_t format; - mps_chain_t chain; - mps_ap_t apamc; - - mycell *p, *q; - int i; - mps_res_t res; - size_t lim0, avail0, lim1, avail1, commit1, lim2, avail2, commit2; - size_t lim3, avail3, commit3, lim4, avail4, commit4; - size_t lim5, avail5, commit5, lim6, avail6, commit6; - - cdie(mps_arena_create(&arena, mps_arena_class_vm(), ARENA_SIZE), - "create arena"); - - 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 stack root"); - - cdie(mps_fmt_create_A(&format, arena, &fmtA), "create format"); - cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - - die(mmqa_pool_create_chain(&poolamc, arena, mps_class_amc(), format, chain), - "create pool"); - - cdie( - mps_ap_create(&apamc, poolamc, mps_rank_exact()), - "create ap"); - - report("lim0", "%d", lim0 = mps_reservoir_limit(arena)); - report("avail0", "%d", avail0 = mps_reservoir_available(arena)); - mps_reservoir_limit_set(arena, (size_t) 0); - - mps_reservoir_limit_set(arena, (size_t) (5ul*1024*1024)); - report("lim1", "%d", lim1 = mps_reservoir_limit(arena)); - report("avail1", "%d", avail1 = mps_reservoir_available(arena)); - report("commit1", "%d", commit1 = arena_committed_and_used(arena)); - report("deficit1", "%d", lim1-avail1); - - mps_reservoir_limit_set(arena, (size_t) (1045)); - report("lim2", "%d", lim2 = mps_reservoir_limit(arena)); - report("avail2", "%d", avail2 = mps_reservoir_available(arena)); - report("commit2", "%d", commit2 = arena_committed_and_used(arena)); - report("deficit2", "%d", lim2-avail2); - - /* set commit limit to whatever is currently committed plus 1 MB */ - - mps_arena_commit_limit_set(arena, arena_committed_and_used(arena)+1024*1024); - mps_reservoir_limit_set(arena, (size_t) (10ul*1024*1024)); - report("lim3", "%d", lim3 = mps_reservoir_limit(arena)); - report("avail3", "%d", avail3 = mps_reservoir_available(arena)); - report("commit3", "%d", commit3 = arena_committed_and_used(arena)); - report("deficit3", "%d", lim3-avail3); - report("spill3", "%d", commit3-mps_arena_commit_limit(arena)); - - /* now raise it by 1/2 MB -- reservoir should grow */ - - mps_arena_commit_limit_set(arena, arena_committed_and_used(arena)+512*1024); - report("lim4", "%d", lim4 = mps_reservoir_limit(arena)); - report("avail4", "%d", avail4 = mps_reservoir_available(arena)); - report("commit4", "%d", commit4 = arena_committed_and_used(arena)); - report("grow4", "%d", avail4-avail3); - report("spill4", "%d", commit4-mps_arena_commit_limit(arena)); - - /* try some allocation -- more than a small amount should fail */ - - i = -1; - p = NULL; - res = MPS_RES_OK; - while (res == MPS_RES_OK) { - res = allocrone(&q, apamc, 10, mps_rank_exact()); - if (res == MPS_RES_OK) { - setref(q, 0, p); - p = q; - } - i++; - } - report("allocfail", "%d", i); - report_res("failres", res); - - /* available shouldn't have changed since before allocation */ - - report("lim5", "%d", lim5 = mps_reservoir_limit(arena)); - report("avail5", "%d", avail5 = mps_reservoir_available(arena)); - report("commit5", "%d", commit5 = arena_committed_and_used(arena)); - report("grow5", "%d", avail5-avail4); - report("spill5", "%d", commit5-mps_arena_commit_limit(arena)); - - /* try some allocation from reservoir -- not much should fail */ - - i = -1; - res = MPS_RES_OK; - while (res == MPS_RES_OK) { - res = reservoir_allocrone(&q, apamc, 10, mps_rank_exact()); - if (res == MPS_RES_OK) { - setref(q, 0, p); - p = q; - } - i++; - } - report("allocfail2", "%d", i); - report_res("failres2", res); - - /* available should have changed now */ - - report("lim6", "%d", lim6 = mps_reservoir_limit(arena)); - report("avail6", "%d", avail6 = mps_reservoir_available(arena)); - report("commit6", "%d", commit6 = arena_committed_and_used(arena)); - 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); - mps_chain_destroy(chain); - mps_fmt_destroy(format); - mps_thread_dereg(thread); - mps_arena_destroy(arena); - comment("Destroyed arena."); -} - - -int main(void) -{ - void *m; - stackpointer=&m; /* hack to get stack pointer */ - - easy_tramp(test); - report("completed", "yes"); - return 0; -} diff --git a/mps/test/function/133.c b/mps/test/function/133.c deleted file mode 100644 index 1f717b11660..00000000000 --- a/mps/test/function/133.c +++ /dev/null @@ -1,151 +0,0 @@ -/* -TEST_HEADER - id = $Id$ - summary = low-memory reservoir tests with commit limit, part II - language = c - link = testlib.o rankfmt.o -OUTPUT_SPEC - allocfail3 > 3000 - failres3 = COMMIT_LIMIT - spill8 <= 0 - spill9 <= 0 - grow9 > 1000000 -END_HEADER -*/ - -#include "testlib.h" -#include "mpscamc.h" -#include "mpsavm.h" -#include "rankfmt.h" - -#define ARENA_SIZE ((size_t) 1024*1024*30) - -#define genCOUNT (3) - -static mps_gen_param_s testChain[genCOUNT] = { - { 6000, 0.90 }, { 8000, 0.65 }, { 16000, 0.50 } }; - -void *stackpointer; - -mps_arena_t arena; -mps_pool_t poolamc; -mps_pool_t poolmv; -mps_thr_t thread; -mps_root_t root; - - mps_chain_t chain; -mps_fmt_t format; -mps_ap_t apamc; - -mps_root_t root; - -static void test(void) { - - mycell *p, *q; - int i; - mps_res_t res; - size_t lim7, avail7, commit7, lim8, avail8, commit8; - size_t lim9, avail9, commit9; - - cdie(mps_arena_create(&arena, mps_arena_class_vm(), ARENA_SIZE), - "create arena"); - - 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 stack root"); - - cdie( - mps_fmt_create_A(&format, arena, &fmtA), - "create format"); - - cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - - cdie( - mps_pool_create(&poolamc, arena, mps_class_amc(), format, chain), - "create pool"); - - cdie( - mps_ap_create(&apamc, poolamc, mps_rank_exact()), - "create ap"); - - mps_arena_commit_limit_set(arena, mps_arena_committed(arena)+1024*1024); - - mps_reservoir_limit_set(arena, 0); - report("lim7", "%d", lim7 = mps_reservoir_limit(arena)); - report("avail7", "%d", avail7 = mps_reservoir_available(arena)); - report("commit7", "%d", commit7 = mps_arena_committed(arena)); - -/* available should be zero, but should be able to allocate with - reservoir permit, until commit_limit is reached -*/ - - i = -1; - p = NULL; - res = MPS_RES_OK; - while (res == MPS_RES_OK) { - res = reservoir_allocrone(&q, apamc, 10, mps_rank_exact()); - if (res == MPS_RES_OK) { - setref(q, 0, p); - p = q; - } - i++; - } - report("allocfail3", "%d", i); - report_res("failres3", res); - -/* should be none left to make available */ - - mps_reservoir_limit_set(arena, 10ul*1024*1024); - report("lim8", "%d", lim8 = mps_reservoir_limit(arena)); - report("avail8", "%d", avail8 = mps_reservoir_available(arena)); - report("commit8", "%d", commit8 = mps_arena_committed(arena)); - report("spill8", "%d", commit8-mps_arena_commit_limit(arena)); - -/* throw away objects and collect world */ - - p = NULL; - q = NULL; - mps_root_destroy(root); - mps_arena_collect(arena); - -/* available should have gone up now */ - - report("lim9", "%d", lim9 = mps_reservoir_limit(arena)); - report("avail9", "%d", avail9 = mps_reservoir_available(arena)); - report("commit9", "%d", commit9 = mps_arena_committed(arena)); - report("grow9", "%d", avail9-avail8); - report("spill9", "%d", commit9-mps_arena_commit_limit(arena)); - -/* destroy everything remaining -*/ - - mps_arena_park(arena); - mps_ap_destroy(apamc); - comment("Destroyed ap."); - - mps_pool_destroy(poolamc); - comment("Destroyed pool."); - - mps_fmt_destroy(format); - comment("Destroyed format."); - - mps_chain_destroy(chain); - comment("Destroyed chain."); - - mps_thread_dereg(thread); - comment("Deregistered thread."); - - mps_arena_destroy(arena); - comment("Destroyed arena."); -} - -int main(void) -{ - void *m; - stackpointer=&m; /* hack to get stack pointer */ - - easy_tramp(test); - report("result", "pass"); - return 0; -} diff --git a/mps/test/function/134.c b/mps/test/function/134.c index 5eee39e5c4f..8bc25359cb9 100644 --- a/mps/test/function/134.c +++ b/mps/test/function/134.c @@ -4,6 +4,7 @@ TEST_HEADER summary = test of ramp allocation with smallish arena (64MB) language = c link = testlib.o rankfmt.o + parameters = ITERATIONS=50000 OUTPUT_SPEC result = pass END_HEADER @@ -16,16 +17,14 @@ END_HEADER #define ARENALIMIT (64) -#define TABSIZE (50000) -#define ENTERRAMP (30000) -#define LEAVERAMP (100000) +#define TABSIZE (ITERATIONS / 2) +#define ENTERRAMP (ITERATIONS / 10) +#define LEAVERAMP (ITERATIONS / 10) #define BACKSIZE (128) #define BACKITER (32) #define RAMPSIZE (128) -#define ITERATIONS (100000ul) - #define RAMP_INTERFACE /* #define COLLECT_WORLD @@ -43,7 +42,7 @@ mps_pool_t poolamc; mps_thr_t thread; mps_root_t root, root1; - mps_chain_t chain; +mps_chain_t chain; mps_fmt_t format; mps_ap_t apamc; @@ -61,11 +60,11 @@ static void alloc_back(void) { static void test(void) { long int i; - long int rsize; + long int rsize = 0; int inramp; - mycell *r, *s; + mycell *r = NULL, *s; cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) 1024*1024*ARENALIMIT), @@ -99,7 +98,7 @@ static void test(void) { inramp = 0; for (i = 0; i < ITERATIONS; i++) { - if (i % 10000 == 0) { + if (i * 10 % ITERATIONS == 0) { comment("%ld of %ld", i, ITERATIONS); } alloc_back(); diff --git a/mps/test/function/135.c b/mps/test/function/135.c index c07b3531f2b..3a7eb3ea4ca 100644 --- a/mps/test/function/135.c +++ b/mps/test/function/135.c @@ -6,12 +6,13 @@ TEST_HEADER link = testlib.o OUTPUT_SPEC count < 10 + errtext = alloc: COMMIT_LIMIT END_HEADER */ #include "testlib.h" #include "mpsavm.h" -#include "mpscmv.h" +#include "mpscmvff.h" void *stackpointer; @@ -38,7 +39,7 @@ static void test(void) { /* create a pool */ - cdie(mps_pool_create(&pool, arena, mps_class_mv(), (size_t) 64, (size_t) 64, (size_t) 64), "pool create"); + cdie(mps_pool_create_k(&pool, arena, mps_class_mvff(), mps_args_none), "pool"); for (i=0; i<200; i++) { report("count", "%i", i); diff --git a/mps/test/function/136.c b/mps/test/function/136.c index b76a2fcf59a..bd873e899b9 100644 --- a/mps/test/function/136.c +++ b/mps/test/function/136.c @@ -5,6 +5,7 @@ TEST_HEADER language = c link = testlib.o OUTPUT_SPEC + assert = true limit < 160000 END_HEADER */ @@ -31,7 +32,6 @@ END_HEADER #include "testlib.h" #include "mpscmvff.h" -#include "mpscmv.h" #include "mpsavm.h" @@ -55,7 +55,7 @@ static void do_test(size_t extendBy, size_t avgSize, size_t align, mps_addr_t p; unsigned int i; unsigned long nLargeObjects = 0, nSmallObjects = 0; - unsigned long largeObjectSize, smallObjectSize; + size_t largeObjectSize, smallObjectSize; largeObjectSize = extendBy; smallObjectSize = align; @@ -73,15 +73,17 @@ static void do_test(size_t extendBy, size_t avgSize, size_t align, "create MVFF pool"); } MPS_ARGS_END(args); - die(mps_pool_create(&pool2, arena, mps_class_mv(), - extendBy, avgSize, /* maxSize */ extendBy), - "create MV pool"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_EXTEND_BY, extendBy); + die(mps_pool_create_k(&pool2, arena, mps_class_mvff(), args), + "create second MVFF pool"); + } MPS_ARGS_END(args); - /* Allocate one small object in pool2 so that its block and span - pools get some initial memory. */ + /* Allocate one small object in pool2 so that its CBS gets 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", + asserts(res == MPS_RES_OK, + "Couldn't allocate first object of size %lu in second pool", (unsigned long)8); /* First we allocate large objects until we run out of memory. */ @@ -129,6 +131,8 @@ static void do_test(size_t extendBy, size_t avgSize, size_t align, } /* MVFF should be failing over from the CBS to the freelist now. */ + res = mps_alloc(&p, pool2, largeObjectSize); + asserts(res != MPS_RES_OK, "unexpectedly have some memory left"); /* Then we free every other large object */ for(i = 0; i < nLargeObjects; i += 2) { @@ -139,7 +143,7 @@ static void do_test(size_t extendBy, size_t avgSize, size_t align, /* Then we allocate in another pool. */ res = mps_alloc(&p, pool2, largeObjectSize); asserts(res == MPS_RES_OK, - "Couldn't allocate one object of size %lu in second pool", + "Couldn't allocate second object of size %lu in second pool", (unsigned long)largeObjectSize); done: @@ -152,14 +156,20 @@ static void test(void) { mps_thr_t thread; int symm; + size_t grainSize = 4096; size_t comlimit; mps_bool_t slotHigh, arenaHigh, firstFit; - cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) (1024*1024*50)), - "create arena"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, 1024*1024*50); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, grainSize); + 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"); - for (comlimit = 512 * 1024; comlimit >= 148 * 1024; comlimit -= 4*1024) { + for (comlimit = 128 * grainSize; comlimit > 0; comlimit -= grainSize) { mps_arena_commit_limit_set(arena, comlimit); report("limit", "%d", comlimit); symm = ranint(8); @@ -167,7 +177,7 @@ static void test(void) arenaHigh = (symm >> 1) & 1; firstFit = (symm & 1); - do_test(4096, 8, 8, slotHigh, arenaHigh, firstFit); + do_test(grainSize, 8, 8, slotHigh, arenaHigh, firstFit); } mps_thread_dereg(thread); diff --git a/mps/test/function/140.c b/mps/test/function/140.c index 2ad53edc583..ae442893a12 100644 --- a/mps/test/function/140.c +++ b/mps/test/function/140.c @@ -4,6 +4,7 @@ TEST_HEADER summary = MVFF low-memory test language = c link = testlib.o + parameters = QUEUES=1000 ITERATIONS=100000 END_HEADER */ @@ -60,13 +61,13 @@ static int chkobj(mps_addr_t a, size_t size, unsigned char val) static void dt(int kind, size_t extendBy, size_t avgSize, size_t align, - size_t mins, size_t maxs, int number, int iter) + unsigned long mins, unsigned long maxs, int number, int iter) { mps_pool_t pool; int i, hd; clock_t time0, time1; size_t size; - int secs; + double secs; asserts(number <= MAXNUMBER, "number too big"); @@ -113,11 +114,11 @@ static void dt(int kind, if (queue[hd].addr != NULL) { asserts(chkobj(queue[hd].addr, queue[hd].size, (unsigned char) (hd%256)), - "corrupt at %x (%s: %x, %x, %x, %x, %x, %i, %i, %i)", + "corrupt at %p (%s: %x, %x, %x, %c%c%c, %lx, %lx, %i, %i)", queue[hd].addr, tdesc[kind], (int) extendBy, (int) avgSize, (int) align, - (int) mins, (int) maxs, number, iter, - slotHigh*100+arenaHigh*10+firstFit); + slotHigh ? 'S' : 's', arenaHigh ? 'A' : 'a', firstFit ? 'F' : 'f', + mins, maxs, number, iter); commentif(comments, "Free %i at %x, size %x", hd, queue[hd].addr, queue[hd].size); mps_free(pool, queue[hd].addr, queue[hd].size); @@ -144,26 +145,26 @@ static void dt(int kind, mps_pool_destroy(pool); time1=clock(); - secs=(int) 100*(time1-time0)/CLOCKS_PER_SEC; + secs=(time1-time0)/(double)CLOCKS_PER_SEC; - comment("%s test (%x, %x, %x, %x, %x, %i, %i, %i) in %i centisecs", + comment("%s test (%x, %x, %x, %c%c%c, %lx, %lx, %i, %i) in %.2f s", tdesc[kind], (int) extendBy, (int) avgSize, (int) align, - (int) mins, (int) maxs, number, iter, - slotHigh*100+arenaHigh*10+firstFit, secs); + slotHigh ? 'S' : 's', arenaHigh ? 'A' : 'a', firstFit ? 'F' : 'f', + mins, maxs, number, iter, secs); } static void test(void) { mps_thr_t thread; - size_t mins; + unsigned long mins; int symm; size_t comlimit; cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) (1024*1024*50)), "create arena"); cdie(mps_thread_reg(&thread, arena), "register thread"); - for (comlimit = 512*1024; comlimit > 0; comlimit -= 4*1024) { - mps_arena_commit_limit_set(arena, comlimit); + for (comlimit = 512*1024; comlimit > 0; comlimit /= 2) { + if (mps_arena_commit_limit_set(arena, comlimit) != MPS_RES_OK) break; report("limit", "%x", comlimit); symm = ranint(8); slotHigh = (symm >> 2) & 1; @@ -172,7 +173,7 @@ static void test(void) mins = ranrange(1, 16); - dt(RANGAP, 64*1024, 32, 8, 4*mins, 4*ranrange(mins, mins*100), 1000, 100000); + dt(RANGAP, 64*1024, 32, 8, 4*mins, 4*ranrange(mins, mins*100), QUEUES, ITERATIONS); } mps_thread_dereg(thread); diff --git a/mps/test/function/149.c b/mps/test/function/149.c deleted file mode 100644 index a26f510ebdc..00000000000 --- a/mps/test/function/149.c +++ /dev/null @@ -1,199 +0,0 @@ -/* -TEST_HEADER - id = $Id$ - summary = SNC low-memory reservoir tests with commit limit - language = c - harness = 2.1 - link = testlib.o rankfmt.o -OUTPUT_SPEC - lim0 = 0 - avail0 = 0 - lim1 > 5000000 - lim1 < 6000000 - defecit1 = 0 - lim2 > 1045 - lim2 < 32768 - defecit2 = 0 - defecit3 > 8000000 - spill3 <= 0 - spill4 <= 0 - grow4 > 500000 - allocfail < 20 - failres = COMMIT_LIMIT - spill5 <= 0 - grow5 = 0 - avail5 > 1500000 - allocfail2 > 5000 - failres2 = COMMIT_LIMIT - shrink6 > 1000000 - spill6 <= 0 - completed = yes -END_HEADER -*/ - -#include "testlib.h" -#include "mpscsnc.h" -#include "mpsavm.h" -#include "rankfmt.h" - -#define ARENA_SIZE ((size_t) 1024*1024*30) - -void *stackpointer; - -mps_arena_t arena; -mps_pool_t poolsnc; -mps_pool_t poolmv; -mps_thr_t thread; -mps_root_t root; - -mps_fmt_t format; -mps_ap_t apsnc; - -mps_root_t root; - -static void test(void) { - - mycell *p, *q; - int i; - mps_res_t res; - size_t lim0, avail0, lim1, avail1, commit1, lim2, avail2, commit2; - size_t lim3, avail3, commit3, lim4, avail4, commit4; - size_t lim5, avail5, commit5, lim6, avail6, commit6; - - cdie(mps_arena_create(&arena, mps_arena_class_vm(), ARENA_SIZE), - "create arena"); - - 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 stack root"); - - cdie( - mps_fmt_create_A(&format, arena, &fmtA), - "create format"); - - cdie( - mps_pool_create(&poolsnc, arena, mps_class_snc(), format), - "create pool"); - - cdie( - mps_ap_create(&apsnc, poolsnc, mps_rank_exact()), - "create ap"); - - report("lim0", "%d", lim0 = mps_reservoir_limit(arena)); - report("avail0", "%d", avail0 = mps_reservoir_available(arena)); - mps_reservoir_limit_set(arena, (size_t) 0); - - mps_reservoir_limit_set(arena, (size_t) (5ul*1024*1024)); - report("lim1", "%d", lim1 = mps_reservoir_limit(arena)); - report("avail1", "%d", avail1 = mps_reservoir_available(arena)); - report("commit1", "%d", commit1 = arena_committed_and_used(arena)); - report("defecit1", "%d", lim1-avail1); - - mps_reservoir_limit_set(arena, (size_t) (1045)); - report("lim2", "%d", lim2 = mps_reservoir_limit(arena)); - report("avail2", "%d", avail2 = mps_reservoir_available(arena)); - report("commit2", "%d", commit2 = arena_committed_and_used(arena)); - report("defecit2", "%d", lim2-avail2); - -/* set commit limit to whatever is currently committed plus 1 MB -*/ - - die(mps_arena_commit_limit_set(arena, arena_committed_and_used(arena)+1024*1024), "commit limit set"); - mps_reservoir_limit_set(arena, (size_t) (10ul*1024*1024)); - report("lim3", "%d", lim3 = mps_reservoir_limit(arena)); - report("avail3", "%d", avail3 = mps_reservoir_available(arena)); - report("commit3", "%d", commit3 = arena_committed_and_used(arena)); - report("defecit3", "%d", lim3-avail3); - report("spill3", "%d", commit3-mps_arena_commit_limit(arena)); - -/* now raise it by 1/2 MB -- reservoir should grow -*/ - - die(mps_arena_commit_limit_set(arena, arena_committed_and_used(arena)+512*1024), "commit limit set"); - report("lim4", "%d", lim4 = mps_reservoir_limit(arena)); - report("avail4", "%d", avail4 = mps_reservoir_available(arena)); - report("commit4", "%d", commit4 = arena_committed_and_used(arena)); - report("grow4", "%d", avail4-avail3); - report("spill4", "%d", commit4-mps_arena_commit_limit(arena)); - -/* try some allocation -- more than a small amount should fail -*/ - - i = -1; - p = NULL; - res = MPS_RES_OK; - while (res == MPS_RES_OK) { - res = allocrone(&q, apsnc, 10, mps_rank_exact()); - if (res == MPS_RES_OK) { - setref(q, 0, p); - p = q; - } - i++; - } - report("allocfail", "%d", i); - report_res("failres", res); - -/* available shouldn't have changed since before allocation -*/ - - report("lim5", "%d", lim5 = mps_reservoir_limit(arena)); - report("avail5", "%d", avail5 = mps_reservoir_available(arena)); - report("commit5", "%d", commit5 = arena_committed_and_used(arena)); - report("grow5", "%d", avail5-avail4); - report("spill5", "%d", commit5-mps_arena_commit_limit(arena)); - -/* try some allocation from reservoir -- not much should fail -*/ - - i = -1; - res = MPS_RES_OK; - while (res == MPS_RES_OK) { - res = reservoir_allocrone(&q, apsnc, 10, mps_rank_exact()); - if (res == MPS_RES_OK) { - setref(q, 0, p); - p = q; - } - i++; - } - report("allocfail2", "%d", i); - report_res("failres2", res); - -/* available should have changed now -*/ - - report("lim6", "%d", lim6 = mps_reservoir_limit(arena)); - report("avail6", "%d", avail6 = mps_reservoir_available(arena)); - report("commit6", "%d", commit6 = arena_committed_and_used(arena)); - 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."); - - mps_ap_destroy(apsnc); - comment("Destroyed ap."); - - mps_pool_destroy(poolsnc); - comment("Destroyed pool."); - - mps_fmt_destroy(format); - comment("Destroyed format."); - - mps_thread_dereg(thread); - comment("Deregistered thread."); - - mps_arena_destroy(arena); - comment("Destroyed arena."); -} - -int main(void) -{ - void *m; - stackpointer=&m; /* hack to get stack pointer */ - - easy_tramp(test); - report("completed", "yes"); - return 0; -} diff --git a/mps/test/function/150.c b/mps/test/function/150.c index c8496f3d662..60bf31fe73b 100644 --- a/mps/test/function/150.c +++ b/mps/test/function/150.c @@ -8,7 +8,7 @@ OUTPUT_SPEC count1 < 50 count2 < 50 collect = true - collect_not_condemned = 0 + collect_not_condemned <= 4096 result = pass END_HEADER */ diff --git a/mps/test/function/153.c b/mps/test/function/153.c index 219d5f50f44..3ed62edf9d9 100644 --- a/mps/test/function/153.c +++ b/mps/test/function/153.c @@ -4,7 +4,7 @@ TEST_HEADER summary = SNC pop-to-NULL test parameterised (request.dylan.170602) language = c link = testlib.o rankfmt.o - parameters = OBJSIZE=(1024) ITERATIONS=(1000) + parameters = OBJSIZE=1024 ITERATIONS=1000 END_HEADER */ diff --git a/mps/test/function/160.c b/mps/test/function/160.c index af5b017e2a7..13659970748 100644 --- a/mps/test/function/160.c +++ b/mps/test/function/160.c @@ -1,7 +1,7 @@ /* TEST_HEADER id = $Id$ - summary = MV fenceposting check + summary = MVFF debug fenceposting check language = c link = testlib.o OUTPUT_SPEC @@ -11,7 +11,7 @@ END_HEADER */ #include "testlib.h" -#include "mpscmv.h" +#include "mpscmvff.h" #include "mpsavm.h" void *stackpointer; @@ -29,10 +29,11 @@ static void test(void) { (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, - (size_t)8192, (size_t)8, (size_t)65536), - "create MV pool"); - + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_POOL_DEBUG_OPTIONS, &debugOpts); + die(mps_pool_create_k(&pool, arena, mps_class_mvff_debug(), args), + "create MVFF pool"); + } MPS_ARGS_END(args); die(mps_alloc(&a, pool, 64), "alloc a"); c = a; diff --git a/mps/test/function/161.c b/mps/test/function/161.c index 23a0fb8be14..64724aea4a0 100644 --- a/mps/test/function/161.c +++ b/mps/test/function/161.c @@ -1,7 +1,7 @@ /* TEST_HEADER id = $Id$ - summary = MV fenceposting check: subfree + summary = MVFF debug fenceposting check: subfree language = c link = testlib.o OUTPUT_SPEC @@ -12,7 +12,7 @@ END_HEADER */ #include "testlib.h" -#include "mpscmv.h" +#include "mpscmvff.h" #include "mpsavm.h" @@ -32,9 +32,11 @@ static void test(void) (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, - (size_t)8192, (size_t)8, (size_t)65536), - "create MVFF pool"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_POOL_DEBUG_OPTIONS, &debugOpts); + die(mps_pool_create_k(&pool, arena, mps_class_mvff_debug(), args), + "create MVFF pool"); + } MPS_ARGS_END(args); die(mps_alloc(&a, pool, 64), "alloc a"); diff --git a/mps/test/function/162.c b/mps/test/function/162.c index 8967b95b269..7d5179fb659 100644 --- a/mps/test/function/162.c +++ b/mps/test/function/162.c @@ -1,7 +1,7 @@ /* TEST_HEADER id = $Id$ - summary = MV fenceposting check: free + summary = MVFF debug fenceposting check: free language = c link = testlib.o OUTPUT_SPEC @@ -11,7 +11,7 @@ END_HEADER */ #include "testlib.h" -#include "mpscmv.h" +#include "mpscmvff.h" #include "mpsavm.h" void *stackpointer; @@ -29,17 +29,19 @@ static void test(void) { (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, - (size_t)8192, (size_t)8, (size_t)65536), - "create MVFF pool"); - - die(mps_alloc(&a, pool, 63), "alloc a"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_POOL_DEBUG_OPTIONS, &debugOpts); + MPS_ARGS_ADD(args, MPS_KEY_ALIGN, 8); + die(mps_pool_create_k(&pool, arena, mps_class_mvff_debug(), args), + "create MVFF pool"); + } MPS_ARGS_END(args); + die(mps_alloc(&a, pool, 64), "alloc a"); c = a; - c += 63; + c += 64; *c = 0; - mps_free(pool, a, 63); + mps_free(pool, a, 64); mps_pool_destroy(pool); mps_thread_dereg(thread); diff --git a/mps/test/function/164.c b/mps/test/function/164.c index fa36db4045b..368bbffd86e 100644 --- a/mps/test/function/164.c +++ b/mps/test/function/164.c @@ -58,13 +58,13 @@ static int chkobj(mps_addr_t a, size_t size, unsigned char val) static void dt(int kind, size_t extendBy, size_t avgSize, size_t align, - size_t mins, size_t maxs, int number, int iter) + unsigned long mins, unsigned long maxs, int number, int iter) { mps_pool_t pool; int i, hd; clock_t time0, time1; size_t size; - int secs; + double secs; asserts(number <= MAXNUMBER, "number too big"); @@ -107,11 +107,11 @@ static void dt(int kind, if (queue[hd].addr != NULL) { asserts(chkobj(queue[hd].addr, queue[hd].size, (unsigned char) (hd%256)), - "corrupt at %x (%s: %x, %x, %x, %x, %x, %i, %i, %i)", + "corrupt at %x (%s: %x, %x, %x, %c%c%c, %lx, %lx, %i, %i)", queue[hd].addr, tdesc[kind], (int) extendBy, (int) avgSize, (int) align, - (int) mins, (int) maxs, number, iter, - slotHigh*100+arenaHigh*10+firstFit); + slotHigh ? 'S' : 's', arenaHigh ? 'A' : 'a', firstFit ? 'F' : 'f', + mins, maxs, number, iter); commentif(comments, "Free %i at %x, size %x", hd, queue[hd].addr, queue[hd].size); mps_free(pool, queue[hd].addr, queue[hd].size); @@ -138,26 +138,26 @@ static void dt(int kind, mps_pool_destroy(pool); time1=clock(); - secs=(int) 100*(time1-time0)/CLOCKS_PER_SEC; + secs=(time1-time0)/(double)CLOCKS_PER_SEC; - comment("%s test (%x, %x, %x, %x, %x, %i, %i, %i) in %i centisecs", + comment("%s test (%x, %x, %x, %c%c%c, %lx, %lx, %i, %i) in %.2f s", tdesc[kind], (int) extendBy, (int) avgSize, (int) align, - (int) mins, (int) maxs, number, iter, - slotHigh*100+arenaHigh*10+firstFit, secs); + slotHigh ? 'S' : 's', arenaHigh ? 'A' : 'a', firstFit ? 'F' : 'f', + mins, maxs, number, iter, secs); } static void test(void) { mps_thr_t thread; - size_t mins; + unsigned long mins; int symm; size_t comlimit; cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) (1024*1024*50)), "create arena"); cdie(mps_thread_reg(&thread, arena), "register thread"); - for (comlimit = 512*1024; comlimit > 0; comlimit -= 4*1024) { - mps_arena_commit_limit_set(arena, comlimit); + for (comlimit = 512*1024; comlimit > 0; comlimit /= 2) { + if (mps_arena_commit_limit_set(arena, comlimit) != MPS_RES_OK) break; report("limit", "%x", comlimit); symm = ranint(8); slotHigh = (symm >> 2) & 1; diff --git a/mps/test/function/165.c b/mps/test/function/165.c index 5bd3bda7a44..167854851a0 100644 --- a/mps/test/function/165.c +++ b/mps/test/function/165.c @@ -30,7 +30,7 @@ static void test(void) mps_pool_t pool; mps_thr_t thread; - unsigned long com0, com1, com2; + size_t com0, com1, com2; /* create a VM arena of 40MB with commit limit of 100MB, i.e. let the arena do the limiting. */ diff --git a/mps/test/function/167.c b/mps/test/function/167.c index c8858668099..fa17c120fb7 100644 --- a/mps/test/function/167.c +++ b/mps/test/function/167.c @@ -30,7 +30,7 @@ static void test(void) mps_pool_t poolhi, poollo; mps_thr_t thread; - unsigned long com0, com1; + size_t com0, com1; /* create a VM arena of 40MB */ diff --git a/mps/test/function/170.c b/mps/test/function/170.c index 28eb7028be7..85a6a89852c 100644 --- a/mps/test/function/170.c +++ b/mps/test/function/170.c @@ -5,7 +5,7 @@ TEST_HEADER language = c link = testlib.o rankfmt.o harness = 2.1 - parameters = EXTEND=65536 AVGSIZE=32 BIGSIZE=(5*1024*1024); + parameters = EXTEND=65536 AVGSIZE=32 BIGSIZE=5*1024*1024 OUTPUT_SPEC completed = yes failed = no diff --git a/mps/test/function/171.c b/mps/test/function/171.c index 5be537d7580..f00de3e87be 100644 --- a/mps/test/function/171.c +++ b/mps/test/function/171.c @@ -4,7 +4,7 @@ TEST_HEADER summary = test of ramp allocation with tiny arena language = c link = testlib.o rankfmt.o - parameters = ARENA=1024*60 + parameters = ARENA=1024*60 ITERATIONS=10000 OUTPUT_SPEC result = pass END_HEADER @@ -17,16 +17,14 @@ END_HEADER #define ARENALIMIT (ARENA) -#define TABSIZE (50000) -#define ENTERRAMP (3000) -#define LEAVERAMP (10000) +#define TABSIZE (ITERATIONS * 5 / 10) +#define ENTERRAMP (ITERATIONS * 3 / 100) +#define LEAVERAMP (ITERATIONS / 10) #define BACKSIZE (128) #define BACKITER (32) #define RAMPSIZE (128) -#define ITERATIONS (100000ul) - #define RAMP_INTERFACE /* #define COLLECT_WORLD @@ -44,7 +42,7 @@ mps_pool_t poolamc; mps_thr_t thread; mps_root_t root, root1; - mps_chain_t chain; +mps_chain_t chain; mps_fmt_t format; mps_ap_t apamc; @@ -62,11 +60,11 @@ static void alloc_back(void) { static void test(void) { long int i; - long int rsize; + long int rsize = 0; int inramp; - mycell *r, *s; + mycell *r = NULL, *s; cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) ARENALIMIT), @@ -100,7 +98,7 @@ static void test(void) { inramp = 0; for (i = 0; i < ITERATIONS; i++) { - if (i % 10000 == 0) { + if (i * 10 % ITERATIONS == 0) { comment("%ld of %ld", i, ITERATIONS); } alloc_back(); diff --git a/mps/test/function/18.c b/mps/test/function/18.c index 45f1a0fe8cc..b4d749640d8 100644 --- a/mps/test/function/18.c +++ b/mps/test/function/18.c @@ -12,7 +12,7 @@ END_HEADER #include "testlib.h" #include "mpsavm.h" #include "mpscamc.h" -#include "mpscmv.h" +#include "mpscmvff.h" #include "newfmt.h" @@ -47,9 +47,7 @@ static void test(void) die(mps_fmt_create_A(&format, arena, &fmtA), "create format"); die(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - die(mps_pool_create(&pool, arena, mps_class_mv(), - (size_t)(1024*32), (size_t)(1024*16), (size_t)(1024*256)), - "create MV pool"); + cdie(mps_pool_create_k(&pool, arena, mps_class_mvff(), mps_args_none), "pool"); do { res = mps_alloc(&q, pool, 64*1024); diff --git a/mps/test/function/19.c b/mps/test/function/19.c index 15fdecd9018..9194524d8c2 100644 --- a/mps/test/function/19.c +++ b/mps/test/function/19.c @@ -11,7 +11,7 @@ END_HEADER #include "testlib.h" #include "mpscamc.h" -#include "mpscmv.h" +#include "mpscmvff.h" #include "mpsavm.h" #include "newfmt.h" @@ -48,9 +48,7 @@ static void test(void) die(mps_fmt_create_A(&format, arena, &fmtA), "create format"); die(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); - die(mps_pool_create(&pool, arena, mps_class_mv(), - (size_t)(1024*32), (size_t)(1024*16), (size_t)(1024*256)), - "create MV pool"); + cdie(mps_pool_create_k(&pool, arena, mps_class_mvff(), mps_args_none), "pool"); while (mps_alloc(&q, pool, 64*1024)==MPS_RES_OK); p = 0; diff --git a/mps/test/function/20.c b/mps/test/function/20.c index 3cec79e7df3..a0280b3f07f 100644 --- a/mps/test/function/20.c +++ b/mps/test/function/20.c @@ -11,7 +11,7 @@ END_HEADER #include "testlib.h" #include "mpscamc.h" -#include "mpscmv.h" +#include "mpscmvff.h" #include "newfmt.h" void *stackpointer; @@ -32,9 +32,7 @@ static void test(void) { 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(), - (size_t)(1024*32), (size_t)(1024*16), (size_t)(1024*256)), - "create MV pool"); + cdie(mps_pool_create_k(&pool, arena, mps_class_mvff(), mps_args_none), "pool"); while (mps_alloc(&q, pool, 64*1024)==MPS_RES_OK); p=0; diff --git a/mps/test/function/200.c b/mps/test/function/200.c index 894c5c2ef6a..1a1812a446e 100644 --- a/mps/test/function/200.c +++ b/mps/test/function/200.c @@ -1,15 +1,16 @@ /* TEST_HEADER id = $Id$ - summary = new MV allocation test + summary = new MVFF allocation test language = c link = testlib.o + parameters = ITERATIONS=10000 END_HEADER */ #include <time.h> #include "testlib.h" -#include "mpscmv.h" +#include "mpscmvff.h" #include "mpsavm.h" #define MAXNUMBER 1000000 @@ -55,24 +56,25 @@ static int chkobj(mps_addr_t a, size_t size, unsigned char val) } static void dt(int kind, - size_t extendBy, size_t avgSize, size_t maxSize, - size_t mins, size_t maxs, int number, int iter) + size_t extendBy, size_t avgSize, + unsigned long mins, unsigned long maxs, int number, int iter) { mps_pool_t pool; int i, hd; clock_t time0, time1; size_t size; - int secs; + double secs; asserts(number <= MAXNUMBER, "number too big"); time0 = clock(); asserts(time0 != -1, "processor time not available"); - die( - mps_pool_create(&pool, arena, mps_class_mv(), - extendBy, avgSize, maxSize), - "create MV pool"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_EXTEND_BY, extendBy); + MPS_ARGS_ADD(args, MPS_KEY_MEAN_SIZE, avgSize); + cdie(mps_pool_create_k(&pool, arena, mps_class_mvff(), args), "pool"); + } MPS_ARGS_END(args); for(hd=0; hd<number; hd++) { @@ -100,10 +102,10 @@ static void dt(int kind, if (queue[hd].addr != NULL) { asserts(chkobj(queue[hd].addr, queue[hd].size, (unsigned char) (hd%256)), - "corrupt at %x (%s: %x, %x, %x, %x, %x, %i, %i)", + "corrupt at %x (%s: %x, %x, %lx, %lx, %i, %i)", queue[hd].addr, - tdesc[kind], (int) extendBy, (int) avgSize, (int) maxSize, - (int) mins, (int) maxs, number, iter); + tdesc[kind], (int) extendBy, (int) avgSize, + mins, maxs, number, iter); mps_free(pool, queue[hd].addr, queue[hd].size); } size = ranrange(mins, maxs); @@ -123,43 +125,43 @@ static void dt(int kind, mps_pool_destroy(pool); time1=clock(); - secs=(int) 100*(time1-time0)/CLOCKS_PER_SEC; + secs=(time1-time0)/(double)CLOCKS_PER_SEC; - comment("%s test (%x, %x, %x, %x, %x, %i, %i) in %i centisecs", - tdesc[kind], (int) extendBy, (int) avgSize, (int) maxSize, - (int) mins, (int) maxs, number, iter, secs); + comment("%s test (%x, %x, %lx, %lx, %i, %i) in %.2f s", + tdesc[kind], (int) extendBy, (int) avgSize, + mins, maxs, number, iter, secs); } static void test(void) { mps_thr_t thread; - size_t mins; + unsigned long mins; cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) (1024*1024*50)), "create arena"); cdie(mps_thread_reg(&thread, arena), "register thread"); mins = sizeof(int); - dt(SEQ, 4096, 32, 64*1024, 8, 9, 5, 1000); - dt(RANGAP, 64, 64, 64, 8, 128, 100, 100000); + dt(SEQ, 4096, 32, 8, 9, 5, ITERATIONS); + dt(RANGAP, 64, 64, 8, 128, 100, ITERATIONS); - dt(DUMMY, 4096, 32, 64*1024, 8, 64, 1000, 1000000); - dt(SEQ, 4096, 32, 64*1024, 8, 64, 1000, 1000000); - dt(RAN, 4096, 32, 64*1024, 8, 64, 1000, 1000000); - dt(SEQGAP, 4096, 32, 64*1024, 8, 64, 1000, 1000000); - dt(RANGAP, 4096, 32, 64*1024, 8, 64, 1000, 1000000); + dt(DUMMY, 4096, 32, 8, 64, 1000, ITERATIONS); + dt(SEQ, 4096, 32, 8, 64, 1000, ITERATIONS); + dt(RAN, 4096, 32, 8, 64, 1000, ITERATIONS); + dt(SEQGAP, 4096, 32, 8, 64, 1000, ITERATIONS); + dt(RANGAP, 4096, 32, 8, 64, 1000, ITERATIONS); - dt(DUMMY, 4096, 1024, 64*1024, 100, 132, 1000, 1000000); - dt(SEQ, 4096, 1024, 64*1024, 100, 132, 1000, 1000000); - dt(RAN, 4096, 1024, 64*1024, 100, 132, 1000, 1000000); - dt(SEQGAP, 4096, 1024, 64*1024, 100, 132, 1000, 1000000); - dt(RANGAP, 4096, 1024, 64*1024, 100, 132, 1000, 1000000); + dt(DUMMY, 4096, 1024, 100, 132, 1000, ITERATIONS); + dt(SEQ, 4096, 1024, 100, 132, 1000, ITERATIONS); + dt(RAN, 4096, 1024, 100, 132, 1000, ITERATIONS); + dt(SEQGAP, 4096, 1024, 100, 132, 1000, ITERATIONS); + dt(RANGAP, 4096, 1024, 100, 132, 1000, ITERATIONS); - dt(DUMMY, 128*1024, 64*1024, 6400*1024, mins, 128*1024, 100, 10000); - dt(SEQ, 128*1024, 64*1024, 6400*1024, mins, 128*1024, 100, 10000); - dt(RAN, 128*1024, 64*1024, 6400*1024, mins, 128*1024, 100, 10000); - dt(SEQGAP, 128*1024, 64*1024, 6400*1024, mins, 128*1024, 100, 10000); - dt(RANGAP, 128*1024, 64*1024, 6400*1024, mins, 128*1024, 100, 10000); + dt(DUMMY, 128*1024, 64*1024, mins, 128*1024, 100, ITERATIONS); + dt(SEQ, 128*1024, 64*1024, mins, 128*1024, 100, ITERATIONS); + dt(RAN, 128*1024, 64*1024, mins, 128*1024, 100, ITERATIONS); + dt(SEQGAP, 128*1024, 64*1024, mins, 128*1024, 100, ITERATIONS); + dt(RANGAP, 128*1024, 64*1024, mins, 128*1024, 100, ITERATIONS); mps_thread_dereg(thread); mps_arena_destroy(arena); diff --git a/mps/test/function/203.c b/mps/test/function/203.c index 207937d1d35..360f6ffd8be 100644 --- a/mps/test/function/203.c +++ b/mps/test/function/203.c @@ -4,6 +4,7 @@ TEST_HEADER summary = new MVT allocation test language = c link = testlib.o + parameters = ITERATIONS=1000 END_HEADER */ @@ -41,7 +42,7 @@ static void setobj(mps_addr_t a, size_t size, unsigned char val) 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; + size = (size + MPS_PF_ALIGN - 1) & ~(MPS_PF_ALIGN - 1); do { MPS_RESERVE_BLOCK(res, *ref, ap, size); @@ -70,14 +71,14 @@ 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_word_t depth, mps_word_t fragLimit, - size_t mins, size_t maxs, int number, int iter) + unsigned long mins, unsigned long maxs, int number, int iter) { mps_pool_t pool; mps_ap_t ap; int i, hd; clock_t time0, time1; size_t size; - int secs; + double secs; asserts(number <= MAXNUMBER, "number too big"); @@ -117,11 +118,11 @@ static void dt(int kind, if (queue[hd].addr != NULL) { asserts(chkobj(queue[hd].addr, queue[hd].size, (unsigned char) (hd%256)), - "corrupt at %x (%s: %x, %x, %x, %i, %i, %x, %x, %i, %i)", + "corrupt at %x (%s: %x, %x, %x, %i, %i, %lx, %lx, %i, %i)", queue[hd].addr, tdesc[kind], (int) minSize, (int) avgSize, (int) maxSize, (int) depth, (int) fragLimit, - (int) mins, (int) maxs, number, iter); + mins, maxs, number, iter); mps_free(pool, queue[hd].addr, queue[hd].size); } size = ranrange(mins, maxs); @@ -142,18 +143,18 @@ static void dt(int kind, mps_pool_destroy(pool); time1=clock(); - secs=(int) 100*(time1-time0)/CLOCKS_PER_SEC; + secs=(time1-time0)/(double)CLOCKS_PER_SEC; - comment("%s test (%x, %x, %x, %i, %i, %x, %x, %i, %i) in %i centisecs", + comment("%s test (%x, %x, %x, %i, %i, %lx, %lx, %i, %i) in %.2f s", tdesc[kind], (int) minSize, (int) avgSize, (int) maxSize, (int) depth, (int) fragLimit, - (int) mins, (int) maxs, number, iter, secs); + mins, maxs, number, iter, secs); } static void test(void) { mps_thr_t thread; - size_t mins; + unsigned long mins; mps_word_t dep, frag; cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) (1024*1024*100)), "create arena"); @@ -167,34 +168,34 @@ static void test(void) comment("Frag: %i", frag); - dt(SEQ, 8, 8, 9, dep, frag, 8, 9, 5, 100); - dt(RANGAP, 64, 64, 64, dep, frag, 8, 128, 100, 10000); + dt(SEQ, 8, 8, 9, dep, frag, 8, 9, 5, ITERATIONS); + dt(RANGAP, 64, 64, 64, dep, frag, 8, 128, 100, ITERATIONS); - 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, 8, 32, 64, dep, frag, 8, 64, 1000, ITERATIONS); + dt(SEQ, 8, 32, 64, dep, frag, 8, 64, 1000, ITERATIONS); + dt(RAN, 8, 32, 64, dep, frag, 8, 64, 1000, ITERATIONS); + dt(SEQGAP, 8, 32, 64, dep, frag, 8, 64, 1000, ITERATIONS); + dt(RANGAP, 8, 32, 64, dep, frag, 8, 64, 1000, ITERATIONS); - 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, 100, 116, 132, dep, frag, 100, 132, 1000, ITERATIONS); + dt(SEQ, 100, 116, 132, dep, frag, 100, 132, 1000, ITERATIONS); + dt(RAN, 100, 116, 132, dep, frag, 100, 132, 1000, ITERATIONS); + dt(SEQGAP, 100, 116, 132, dep, frag, 100, 132, 1000, ITERATIONS); + dt(RANGAP, 100, 116, 132, dep, frag, 100, 132, 1000, ITERATIONS); - 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); + dt(DUMMY, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, ITERATIONS); + dt(SEQ, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, ITERATIONS); + dt(RAN, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, ITERATIONS); + dt(SEQGAP, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, ITERATIONS); + dt(RANGAP, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, ITERATIONS); /* try again using exceptional obj for anything over 16K */ - 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); + dt(DUMMY, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, ITERATIONS); + dt(SEQ, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, ITERATIONS); + dt(RAN, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, ITERATIONS); + dt(SEQGAP, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, ITERATIONS); + dt(RANGAP, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, ITERATIONS); } diff --git a/mps/test/function/204.c b/mps/test/function/204.c index d1a00731086..73d087c0adf 100644 --- a/mps/test/function/204.c +++ b/mps/test/function/204.c @@ -4,6 +4,7 @@ TEST_HEADER summary = new MVT allocation test, extra shallow language = c link = testlib.o + parameters = ITERATIONS=1000 END_HEADER */ @@ -41,7 +42,7 @@ static void setobj(mps_addr_t a, size_t size, unsigned char val) 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; + size = (size + MPS_PF_ALIGN - 1) & ~ (MPS_PF_ALIGN - 1); do { MPS_RESERVE_BLOCK(res, *ref, ap, size); @@ -70,14 +71,14 @@ 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_word_t depth, mps_word_t fragLimit, - size_t mins, size_t maxs, int number, int iter) + unsigned long mins, unsigned long maxs, int number, int iter) { mps_pool_t pool; mps_ap_t ap; int i, hd; clock_t time0, time1; size_t size; - int secs; + double secs; asserts(number <= MAXNUMBER, "number too big"); @@ -117,11 +118,11 @@ static void dt(int kind, if (queue[hd].addr != NULL) { asserts(chkobj(queue[hd].addr, queue[hd].size, (unsigned char) (hd%256)), - "corrupt at %x (%s: %x, %x, %x, %i, %i, %x, %x, %i, %i)", + "corrupt at %x (%s: %x, %x, %x, %i, %i, %lx, %lx, %i, %i)", queue[hd].addr, tdesc[kind], (int) minSize, (int) avgSize, (int) maxSize, (int) depth, (int) fragLimit, - (int) mins, (int) maxs, number, iter); + mins, maxs, number, iter); mps_free(pool, queue[hd].addr, queue[hd].size); } size = ranrange(mins, maxs); @@ -142,18 +143,18 @@ static void dt(int kind, mps_pool_destroy(pool); time1=clock(); - secs=(int) 100*(time1-time0)/CLOCKS_PER_SEC; + secs=(time1-time0)/(double)CLOCKS_PER_SEC; - comment("%s test (%x, %x, %x, %i, %i, %x, %x, %i, %i) in %i centisecs", + comment("%s test (%x, %x, %x, %i, %i, %lx, %lx, %i, %i) in %.2f s", tdesc[kind], (int) minSize, (int) avgSize, (int) maxSize, (int) depth, (int) fragLimit, - (int) mins, (int) maxs, number, iter, secs); + mins, maxs, number, iter, secs); } static void test(void) { mps_thr_t thread; - size_t mins; + unsigned long mins; mps_word_t dep, frag; cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) (1024*1024*100)), "create arena"); @@ -167,34 +168,34 @@ static void test(void) comment("Frag: %i", frag); - dt(SEQ, 8, 8, 9, dep, frag, 8, 9, 5, 100); - dt(RANGAP, 64, 64, 64, dep, frag, 8, 128, 100, 10000); + dt(SEQ, 8, 8, 9, dep, frag, 8, 9, 5, ITERATIONS); + dt(RANGAP, 64, 64, 64, dep, frag, 8, 128, 100, ITERATIONS); - 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, 8, 32, 64, dep, frag, 8, 64, 1000, ITERATIONS); + dt(SEQ, 8, 32, 64, dep, frag, 8, 64, 1000, ITERATIONS); + dt(RAN, 8, 32, 64, dep, frag, 8, 64, 1000, ITERATIONS); + dt(SEQGAP, 8, 32, 64, dep, frag, 8, 64, 1000, ITERATIONS); + dt(RANGAP, 8, 32, 64, dep, frag, 8, 64, 1000, ITERATIONS); - 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, 100, 116, 132, dep, frag, 100, 132, 1000, ITERATIONS); + dt(SEQ, 100, 116, 132, dep, frag, 100, 132, 1000, ITERATIONS); + dt(RAN, 100, 116, 132, dep, frag, 100, 132, 1000, ITERATIONS); + dt(SEQGAP, 100, 116, 132, dep, frag, 100, 132, 1000, ITERATIONS); + dt(RANGAP, 100, 116, 132, dep, frag, 100, 132, 1000, ITERATIONS); - 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); + dt(DUMMY, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, ITERATIONS); + dt(SEQ, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, ITERATIONS); + dt(RAN, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, ITERATIONS); + dt(SEQGAP, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, ITERATIONS); + dt(RANGAP, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, ITERATIONS); /* try again using exceptional obj for anything over 16K */ - 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); + dt(DUMMY, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, ITERATIONS); + dt(SEQ, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, ITERATIONS); + dt(RAN, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, ITERATIONS); + dt(SEQGAP, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, ITERATIONS); + dt(RANGAP, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, ITERATIONS); } diff --git a/mps/test/function/205.c b/mps/test/function/205.c index d067d6a5ada..ff94d098ca2 100644 --- a/mps/test/function/205.c +++ b/mps/test/function/205.c @@ -4,6 +4,7 @@ TEST_HEADER summary = new MVT allocation test, extra deep language = c link = testlib.o + parameters = ITERATIONS=1000 END_HEADER */ @@ -41,7 +42,7 @@ static void setobj(mps_addr_t a, size_t size, unsigned char val) 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; + size = (size + MPS_PF_ALIGN - 1) & ~ (MPS_PF_ALIGN - 1); do { MPS_RESERVE_BLOCK(res, *ref, ap, size); @@ -70,14 +71,14 @@ 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_word_t depth, mps_word_t fragLimit, - size_t mins, size_t maxs, int number, int iter) + unsigned long mins, unsigned long maxs, int number, int iter) { mps_pool_t pool; mps_ap_t ap; int i, hd; clock_t time0, time1; size_t size; - int secs; + double secs; asserts(number <= MAXNUMBER, "number too big"); @@ -117,11 +118,11 @@ static void dt(int kind, if (queue[hd].addr != NULL) { asserts(chkobj(queue[hd].addr, queue[hd].size, (unsigned char) (hd%256)), - "corrupt at %x (%s: %x, %x, %x, %i, %i, %x, %x, %i, %i)", + "corrupt at %x (%s: %x, %x, %x, %i, %i, %lx, %lx, %i, %i)", queue[hd].addr, tdesc[kind], (int) minSize, (int) avgSize, (int) maxSize, (int) depth, (int) fragLimit, - (int) mins, (int) maxs, number, iter); + mins, maxs, number, iter); mps_free(pool, queue[hd].addr, queue[hd].size); } size = ranrange(mins, maxs); @@ -142,18 +143,18 @@ static void dt(int kind, mps_pool_destroy(pool); time1=clock(); - secs=(int) 100*(time1-time0)/CLOCKS_PER_SEC; + secs=(time1-time0)/(double)CLOCKS_PER_SEC; - comment("%s test (%x, %x, %x, %i, %i, %x, %x, %i, %i) in %i centisecs", + comment("%s test (%x, %x, %x, %i, %i, %lx, %lx, %i, %i) in %.2f s", tdesc[kind], (int) minSize, (int) avgSize, (int) maxSize, (int) depth, (int) fragLimit, - (int) mins, (int) maxs, number, iter, secs); + mins, maxs, number, iter, secs); } static void test(void) { mps_thr_t thread; - size_t mins; + unsigned long mins; mps_word_t dep, frag; cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) (1024*1024*100)), "create arena"); @@ -167,34 +168,34 @@ static void test(void) comment("Frag: %i", frag); - dt(SEQ, 8, 8, 9, dep, frag, 8, 9, 5, 100); - dt(RANGAP, 64, 64, 64, dep, frag, 8, 128, 100, 10000); + dt(SEQ, 8, 8, 9, dep, frag, 8, 9, 5, ITERATIONS); + dt(RANGAP, 64, 64, 64, dep, frag, 8, 128, 100, ITERATIONS); - 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, 8, 32, 64, dep, frag, 8, 64, 1000, ITERATIONS); + dt(SEQ, 8, 32, 64, dep, frag, 8, 64, 1000, ITERATIONS); + dt(RAN, 8, 32, 64, dep, frag, 8, 64, 1000, ITERATIONS); + dt(SEQGAP, 8, 32, 64, dep, frag, 8, 64, 1000, ITERATIONS); + dt(RANGAP, 8, 32, 64, dep, frag, 8, 64, 1000, ITERATIONS); - 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, 100, 116, 132, dep, frag, 100, 132, 1000, ITERATIONS); + dt(SEQ, 100, 116, 132, dep, frag, 100, 132, 1000, ITERATIONS); + dt(RAN, 100, 116, 132, dep, frag, 100, 132, 1000, ITERATIONS); + dt(SEQGAP, 100, 116, 132, dep, frag, 100, 132, 1000, ITERATIONS); + dt(RANGAP, 100, 116, 132, dep, frag, 100, 132, 1000, ITERATIONS); - 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); + dt(DUMMY, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, ITERATIONS); + dt(SEQ, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, ITERATIONS); + dt(RAN, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, ITERATIONS); + dt(SEQGAP, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, ITERATIONS); + dt(RANGAP, mins, 60*1024, 120*1024, dep, frag, mins, 128*1024, 100, ITERATIONS); /* try again using exceptional obj for anything over 16K */ - 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); + dt(DUMMY, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, ITERATIONS); + dt(SEQ, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, ITERATIONS); + dt(RAN, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, ITERATIONS); + dt(SEQGAP, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, ITERATIONS); + dt(RANGAP, mins, 8*1024, 16*1024, dep, frag, mins, 128*1024, 100, ITERATIONS); } diff --git a/mps/test/function/206.c b/mps/test/function/206.c index 95812038290..f9a40cf7f1d 100644 --- a/mps/test/function/206.c +++ b/mps/test/function/206.c @@ -4,6 +4,7 @@ TEST_HEADER summary = new MVFF allocation test language = c link = testlib.o + parameters = QUEUES=100 ITERATIONS=1000 END_HEADER */ @@ -58,13 +59,13 @@ static int chkobj(mps_addr_t a, size_t size, unsigned char val) static void dt(int kind, size_t extendBy, size_t avgSize, size_t align, - size_t mins, size_t maxs, int number, int iter) + unsigned long mins, unsigned long maxs, int number, int iter) { mps_pool_t pool; int i, hd; clock_t time0, time1; size_t size; - int secs; + double secs; asserts(number <= MAXNUMBER, "number too big"); @@ -74,7 +75,7 @@ static void dt(int kind, die( mps_pool_create(&pool, arena, mps_class_mvff(), extendBy, avgSize, align, slotHigh, arenaHigh, firstFit), - "create EPDR pool"); + "create MVFF pool"); for(hd=0; hd<number; hd++) { @@ -102,11 +103,11 @@ static void dt(int kind, if (queue[hd].addr != NULL) { asserts(chkobj(queue[hd].addr, queue[hd].size, (unsigned char) (hd%256)), - "corrupt at %x (%s: %x, %x, %x, %x, %x, %i, %i, %i)", + "corrupt at %p (%s: %x, %x, %x, %c%c%c, %lx, %lx, %i, %i)", queue[hd].addr, tdesc[kind], (int) extendBy, (int) avgSize, (int) align, - (int) mins, (int) maxs, number, iter, - slotHigh*100+arenaHigh*10+firstFit); + slotHigh ? 'S' : 's', arenaHigh ? 'A' : 'a', firstFit ? 'F' : 'f', + mins, maxs, number, iter); mps_free(pool, queue[hd].addr, queue[hd].size); } size = ranrange(mins, maxs); @@ -126,52 +127,36 @@ static void dt(int kind, mps_pool_destroy(pool); time1=clock(); - secs=(int) 100*(time1-time0)/CLOCKS_PER_SEC; + secs=(time1-time0)/(double)CLOCKS_PER_SEC; - comment("%s test (%x, %x, %x, %x, %x, %i, %i, %i) in %i centisecs", + comment("%s test (%x, %x, %x, %c%c%c, %lx, %lx, %i, %i) in %.2f s", tdesc[kind], (int) extendBy, (int) avgSize, (int) align, - (int) mins, (int) maxs, number, iter, - slotHigh*100+arenaHigh*10+firstFit, secs); + slotHigh ? 'S' : 's', arenaHigh ? 'A' : 'a', firstFit ? 'F' : 'f', + mins, maxs, number, iter, secs); } static void test(void) { mps_thr_t thread; - size_t mins; - int symm; + size_t avgSize; + size_t align = sizeof(void*); + unsigned long extendBy, minSize = sizeof(int), maxSize; + int i, j, kind; cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) (1024*1024*50)), "create arena"); cdie(mps_thread_reg(&thread, arena), "register thread"); - mins = sizeof(int); - - for (symm = 0; symm < 8; symm++) { - - slotHigh = (symm >> 2) & 1; - arenaHigh = (symm >> 1) & 1; - firstFit = (symm & 1); - - dt(SEQ, 4096, 32, 8, 8, 9, 5, 1000); - dt(RANGAP, 64, 64, 8, 8, 128, 100, 100000); - - dt(DUMMY, 4096, 32, 8, 8, 64, 1000, 1000000); - dt(SEQ, 4096, 32, 8, 8, 64, 1000, 1000000); - dt(RAN, 4096, 32, 8, 8, 64, 1000, 1000000); - dt(SEQGAP, 4096, 32, 8, 8, 64, 1000, 1000000); - dt(RANGAP, 4096, 32, 8, 8, 64, 1000, 1000000); - - dt(DUMMY, 4096, 1024, 8, 100, 132, 1000, 1000000); - dt(SEQ, 4096, 1024, 8, 100, 132, 1000, 1000000); - dt(RAN, 4096, 1024, 8, 100, 132, 1000, 1000000); - dt(SEQGAP, 4096, 1024, 8, 100, 132, 1000, 1000000); - dt(RANGAP, 4096, 1024, 8, 100, 132, 1000, 1000000); - - dt(DUMMY, 128*1024, 64*1024, 8, mins, 128*1024, 100, 10000); - dt(SEQ, 128*1024, 64*1024, 8, mins, 128*1024, 100, 10000); - dt(RAN, 128*1024, 64*1024, 8, mins, 128*1024, 100, 10000); - dt(SEQGAP, 128*1024, 64*1024, 8, mins, 128*1024, 100, 10000); - dt(RANGAP, 128*1024, 64*1024, 8, mins, 128*1024, 100, 10000); + for (i = 0; i < 5 * 2 * 2 * 2 * 2 * 2; ++i) { + j = i; + slotHigh = j % 2; j /= 2; + arenaHigh = j % 2; j /= 2; + firstFit = j % 2; j /= 2; + extendBy = j % 2 ? 4096 : 65536; j /= 2; + avgSize = j % 2 ? 32 : extendBy / 2; + maxSize = j % 2 ? 64 : extendBy; j /= 2; + kind = j % 5; j /= 5; + dt(kind, extendBy, avgSize, align, minSize, maxSize, QUEUES, ITERATIONS); } mps_thread_dereg(thread); diff --git a/mps/test/function/207.c b/mps/test/function/207.c index bb3c4a2b619..c68ac5757af 100644 --- a/mps/test/function/207.c +++ b/mps/test/function/207.c @@ -58,13 +58,13 @@ static int chkobj(mps_addr_t a, size_t size, unsigned char val) static void dt(int kind, size_t extendBy, size_t avgSize, size_t align, - size_t mins, size_t maxs, int number, int iter) + unsigned long mins, unsigned long maxs, int number, int iter) { mps_pool_t pool; int i, hd; clock_t time0, time1; size_t size; - int secs; + double secs; asserts(number <= MAXNUMBER, "number too big"); @@ -107,11 +107,11 @@ static void dt(int kind, if (queue[hd].addr != NULL) { asserts(chkobj(queue[hd].addr, queue[hd].size, (unsigned char) (hd%256)), - "corrupt at %x (%s: %x, %x, %x, %x, %x, %i, %i, %i)", + "corrupt at %x (%s: %x, %x, %x, %c%c%c, %lx, %lx, %i, %i)", queue[hd].addr, tdesc[kind], (int) extendBy, (int) avgSize, (int) align, - (int) mins, (int) maxs, number, iter, - slotHigh*100+arenaHigh*10+firstFit); + slotHigh ? 'S' : 's', arenaHigh ? 'A' : 'a', firstFit ? 'F' : 'f', + mins, maxs, number, iter); commentif(comments, "Free %i at %x, size %x", hd, queue[hd].addr, queue[hd].size); mps_free(pool, queue[hd].addr, queue[hd].size); @@ -138,26 +138,26 @@ static void dt(int kind, mps_pool_destroy(pool); time1=clock(); - secs=(int) 100*(time1-time0)/CLOCKS_PER_SEC; + secs=(time1-time0)/(double)CLOCKS_PER_SEC; - comment("%s test (%x, %x, %x, %x, %x, %i, %i, %i) in %i centisecs", + comment("%s test (%x, %x, %x, %c%c%c, %lx, %lx, %i, %i) in %.2f s", tdesc[kind], (int) extendBy, (int) avgSize, (int) align, - (int) mins, (int) maxs, number, iter, - slotHigh*100+arenaHigh*10+firstFit, secs); + slotHigh ? 'S' : 's', arenaHigh ? 'A' : 'a', firstFit ? 'F' : 'f', + mins, maxs, number, iter, secs); } static void test(void) { mps_thr_t thread; - size_t mins; + unsigned long mins; int symm; size_t comlimit; cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) (1024*1024*50)), "create arena"); cdie(mps_thread_reg(&thread, arena), "register thread"); - for (comlimit = 512*1024; comlimit > 0; comlimit -= 4*1024) { - mps_arena_commit_limit_set(arena, comlimit); + for (comlimit = 512*1024; comlimit > 0; comlimit /= 2) { + if (mps_arena_commit_limit_set(arena, comlimit) != MPS_RES_OK) break; report("limit", "%x", comlimit); symm = ranint(8); slotHigh = (symm >> 2) & 1; diff --git a/mps/test/function/21.c b/mps/test/function/21.c index 7b493f6602f..dee84e417f3 100644 --- a/mps/test/function/21.c +++ b/mps/test/function/21.c @@ -1,14 +1,15 @@ /* TEST_HEADER id = $Id$ - summary = allocate large promise, make it small, repeat + summary = allocate large object, free its middle, repeat language = c link = testlib.o + parameters = OBJECTS=2000 END_HEADER */ #include "testlib.h" -#include "mpscmv.h" +#include "mpscmvff.h" static void test(void) { mps_arena_t arena; @@ -16,18 +17,18 @@ static void test(void) { mps_addr_t q; int p; - die(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create"); + die(mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none), "create"); - die(mps_pool_create(&pool, arena, mps_class_mv(), - (size_t)(1024*32), (size_t)(1024*16), (size_t)(1024*256)), - "create MV pool"); + cdie(mps_pool_create_k(&pool, arena, mps_class_mvff(), mps_args_none), "pool"); - for (p=0; p<2000; p++) { - die(mps_alloc(&q, pool, 1024*1024), "alloc"); - q = (mps_addr_t) ((char *) q + 8); - mps_free(pool, q, 256*1024-8); - report("promise", "%i", p); + for (p=0; p<OBJECTS; p++) { + die(mps_alloc(&q, pool, 1024), "alloc"); + q = (mps_addr_t) ((char *) q + MPS_PF_ALIGN); + mps_free(pool, q, 256-MPS_PF_ALIGN); } + + mps_pool_destroy(pool); + mps_arena_destroy(arena); } int main(void) { diff --git a/mps/test/function/215.c b/mps/test/function/215.c index 14ad7a1b808..52754ab0a11 100644 --- a/mps/test/function/215.c +++ b/mps/test/function/215.c @@ -4,6 +4,7 @@ TEST_HEADER summary = test of ramp allocation language = c link = testlib.o rankfmt.o + parameters = ITERATIONS=10000 OUTPUT_SPEC result = pass END_HEADER @@ -16,16 +17,14 @@ END_HEADER #define ARENALIMIT (200) -#define TABSIZE (50000) -#define ENTERRAMP (30000) -#define LEAVERAMP (100000) +#define TABSIZE (ITERATIONS * 5 / 10) +#define ENTERRAMP (ITERATIONS * 3 / 100) +#define LEAVERAMP (ITERATIONS / 10) #define BACKSIZE (128) #define BACKITER (32) #define RAMPSIZE (128) -#define ITERATIONS (1000000ul) - #define RAMP_INTERFACE /* #define COLLECT_WORLD @@ -61,12 +60,12 @@ static void alloc_back(void) { static void test(void) { long int i; - long int rsize; + long int rsize = 0; mps_message_t message; int inramp; - mycell *r, *s; + mycell *r = NULL, *s; cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) 1024*1024*ARENALIMIT), @@ -102,7 +101,7 @@ static void test(void) { inramp = 0; for (i = 0; i < ITERATIONS; i++) { - if (i % 10000 == 0) { + if (i * 10 % ITERATIONS == 0) { comment("%ld of %ld", i, ITERATIONS); } alloc_back(); @@ -139,7 +138,7 @@ static void test(void) { } } if(mps_message_get(&message, arena, mps_message_type_gc())) { - unsigned long live, condemned, notCondemned; + size_t live, condemned, notCondemned; live = mps_message_gc_live_size(arena, message); condemned = mps_message_gc_condemned_size(arena, message); notCondemned = diff --git a/mps/test/function/22.c b/mps/test/function/22.c index 8927643e49f..3b2ce66f291 100644 --- a/mps/test/function/22.c +++ b/mps/test/function/22.c @@ -1,14 +1,15 @@ /* TEST_HEADER id = $Id$ - summary = allocate large promise, make it small, repeat interleaved + summary = allocate large object, free its middle, repeat interleaved language = c link = testlib.o + parameters = OBJECTS=2000 END_HEADER */ #include "testlib.h" -#include "mpscmv.h" +#include "mpscmvff.h" static void test(void) { mps_arena_t arena; @@ -16,20 +17,20 @@ static void test(void) { mps_addr_t q, r; int p; - die(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create"); + die(mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none), "create"); - die(mps_pool_create(&pool, arena, mps_class_mv(), - (size_t)(1024*32), (size_t)(1024*16), (size_t)(1024*256)), - "create MV pool"); + cdie(mps_pool_create_k(&pool, arena, mps_class_mvff(), mps_args_none), "pool"); - die(mps_alloc(&q, pool, 1024*1024), "alloc"); + die(mps_alloc(&q, pool, 1024), "alloc"); - for (p=0; p<2000; p++) { - report("promise", "%i", p); - die(mps_alloc(&r, pool, 1024*1024), "alloc"); - mps_free(pool, q, 256*1024-8); - q = (mps_addr_t) ((char *) r + 8); + for (p=0; p<OBJECTS; p++) { + die(mps_alloc(&r, pool, 1024), "alloc"); + mps_free(pool, q, 256-MPS_PF_ALIGN); + q = (mps_addr_t) ((char *) r + MPS_PF_ALIGN); } + + mps_pool_destroy(pool); + mps_arena_destroy(arena); } int main(void) { diff --git a/mps/test/function/223.c b/mps/test/function/223.c index 6a402d1c831..ddb88115d0d 100644 --- a/mps/test/function/223.c +++ b/mps/test/function/223.c @@ -4,6 +4,7 @@ TEST_HEADER summary = test of ramp allocation language = c link = testlib.o rankfmt.o + parameters = ITERATIONS=50000 OUTPUT_SPEC result = pass END_HEADER @@ -16,16 +17,14 @@ END_HEADER #define ARENALIMIT (200) -#define TABSIZE (50000) -#define ENTERRAMP (30000) -#define LEAVERAMP (100000) +#define TABSIZE (ITERATIONS / 2) +#define ENTERRAMP (ITERATIONS / 10) +#define LEAVERAMP (ITERATIONS / 10) #define BACKSIZE (128) #define BACKITER (32) #define RAMPSIZE (128) -#define ITERATIONS (100000ul) - #define RAMP_INTERFACE /* #define COLLECT_WORLD @@ -61,12 +60,12 @@ static void alloc_back(void) { static void test(void) { long int i; - long int rsize; + long int rsize = 0; mps_message_t message; int inramp; - mycell *r, *s; + mycell *r = NULL, *s; cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) 1024*1024*ARENALIMIT), @@ -102,7 +101,7 @@ static void test(void) { inramp = 0; for (i = 0; i < ITERATIONS; i++) { - if (i % 10000 == 0) { + if (i * 10 % ITERATIONS == 0) { comment("%ld of %ld", i, ITERATIONS); } alloc_back(); @@ -139,7 +138,7 @@ static void test(void) { } } if(mps_message_get(&message, arena, mps_message_type_gc())) { - unsigned long live, condemned, notCondemned; + size_t live, condemned, notCondemned; 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); diff --git a/mps/test/function/224.c b/mps/test/function/224.c index ec828bd9026..1e52e07c5da 100644 --- a/mps/test/function/224.c +++ b/mps/test/function/224.c @@ -1,21 +1,17 @@ /* TEST_HEADER id = $Id$ - summary = MV allocate large promise, make it small, repeat + summary = MVFF allocate large promise, make it small, repeat language = c link = testlib.o harness = 2.5 - parameters = EXTENDBY=65536 AVGSIZE=32 PROMISE=64 ITERATE=2000 -OUTPUT_SPEC - errtext = alloc: COMMIT_LIMIT + parameters = PROMISE=65536 ITERATE=2000 END_HEADER - -This one is supposed to fail, telling us that MV is badly fragmented. */ -#include "testlib.h" -#include "mpscmv.h" #include "mpsavm.h" +#include "mpscmvff.h" +#include "testlib.h" #define VMSIZE ((size_t) 30*1024*1024) @@ -30,15 +26,13 @@ static void test(void) 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(), - (size_t)EXTENDBY, (size_t)AVGSIZE, (size_t)EXTENDBY), + die(mps_pool_create_k(&pool, arena, mps_class_mvff(), mps_args_none), "pool create"); for (p=0; p<ITERATE; p++) { - die(mps_alloc(&q, pool, PROMISE*1024), "alloc"); - q = (mps_addr_t) ((char *) q + 8); - mps_free(pool, q, PROMISE*1024-8); + die(mps_alloc(&q, pool, PROMISE), "alloc"); + q = (char *)q + MPS_PF_ALIGN; + mps_free(pool, q, PROMISE - MPS_PF_ALIGN); report("promise", "%i", p); } diff --git a/mps/test/function/226.c b/mps/test/function/226.c index 185a4b97298..076e4ceb9ea 100644 --- a/mps/test/function/226.c +++ b/mps/test/function/226.c @@ -5,13 +5,13 @@ TEST_HEADER language = c link = testlib.o rankfmt.o harness = 3.0 - parameters = MAXLDS=1000 MAXMERGE=100 BLATPERCENT=90 JUNK=100 AMBIGHOLD=900 + parameters = MAXLDS=1000 MAXMERGE=20 BLATPERCENT=90 JUNK=100 AMBIGHOLD=900 END_HEADER */ #include "testlib.h" #include "mpscawl.h" -#include "mpscmv.h" +#include "mpscmvff.h" #include "mpscamc.h" #include "mpsavm.h" #include "rankfmt.h" @@ -58,7 +58,7 @@ static void mergelds(int merge) { } } -static void blat(mps_ap_t apamc, int percent) { +static void blat(mps_ap_t apamc, unsigned percent) { int i; for (i=0; i < MAXLDS; i++) { if (ranint(100) < percent) { @@ -71,7 +71,7 @@ static void blat(mps_ap_t apamc, int percent) { } static void test(void) { - mps_pool_t poolmv, poolawl, poolamc; + mps_pool_t poolmvff, poolawl, poolamc; mps_thr_t thread; mps_root_t root0, root1, root2; mps_addr_t p; @@ -112,9 +112,8 @@ static void test(void) { mps_pool_create(&poolawl, arena, mps_class_awl(), format, getassociated), "create awl pool"); - cdie(mps_pool_create(&poolmv, arena, mps_class_mv(), - (size_t)0x4000, (size_t)128, (size_t)0x4000), - "create mv pool"); + cdie(mps_pool_create_k(&poolmvff, arena, mps_class_mvff(), mps_args_none), + "create MVFF pool"); cdie( mps_ap_create(&apawl, poolawl, mps_rank_exact()), @@ -135,9 +134,9 @@ static void test(void) { */ for (i=0; i < MAXLDS; i++) { - mps_alloc(&p, poolmv, sizeof(mps_ld_s)); + mps_alloc(&p, poolmvff, sizeof(mps_ld_s)); lds[i] = (mps_ld_t) p; - mps_alloc(&p, poolmv, sizeof(mps_ld_s)); + mps_alloc(&p, poolmvff, sizeof(mps_ld_s)); ldm[i] = (mps_ld_t) p; } @@ -176,7 +175,7 @@ static void test(void) { mps_ap_destroy(apamc); comment("Destroyed aps."); - mps_pool_destroy(poolmv); + mps_pool_destroy(poolmvff); mps_pool_destroy(poolamc); mps_pool_destroy(poolawl); comment("Destroyed pools."); diff --git a/mps/test/function/227.c b/mps/test/function/227.c index 09b8cb98e30..35fe35b3d02 100644 --- a/mps/test/function/227.c +++ b/mps/test/function/227.c @@ -4,6 +4,7 @@ TEST_HEADER summary = allocate in 2 arenas language = c link = testlib.o rankfmt.o + parameters = ITERATIONS=10000 OUTPUT_SPEC result = pass END_HEADER @@ -20,16 +21,14 @@ END_HEADER #define ARENALIMIT (100) -#define TABSIZE (50000) -#define ENTERRAMP (30000) -#define LEAVERAMP (100000) +#define TABSIZE (ITERATIONS / 2) +#define ENTERRAMP (ITERATIONS / 10) +#define LEAVERAMP (ITERATIONS / 10) #define BACKSIZE (32) #define BACKITER (32) #define RAMPSIZE (128) -#define ITERATIONS (1000000ul) - #define RAMP_INTERFACE /* #define COLLECT_WORLD @@ -59,11 +58,11 @@ static void alloc_back(void) { static void test(void) { long int i; - long int rsize; + long int rsize = 0; int inramp; - mycell *r1, *r2, *s1, *s2; + mycell *r1 = NULL, *r2 = NULL, *s1, *s2; cdie(mps_arena_create(&arena1, mps_arena_class_vm(), (size_t) 1024*1024*ARENALIMIT), "create arena"); @@ -117,7 +116,7 @@ static void test(void) { inramp = 0; for (i = 0; i < ITERATIONS; i++) { - if (i % 10000 == 0) { + if (i * 10 % ITERATIONS == 0) { comment("%ld of %ld", i, ITERATIONS); } alloc_back(); diff --git a/mps/test/function/23.c b/mps/test/function/23.c index 2046aa56b92..58559c20d09 100644 --- a/mps/test/function/23.c +++ b/mps/test/function/23.c @@ -1,7 +1,7 @@ /* TEST_HEADER id = $Id$ - summary = ensure allocation in MV pool causes collection + summary = ensure allocation in MVFF pool causes collection language = c link = newfmt.o testlib.o OUTPUT_SPEC @@ -14,7 +14,7 @@ END_HEADER */ #include "testlib.h" -#include "mpscmv.h" +#include "mpscmvff.h" #include "mpscamc.h" #include "mpsavm.h" #include "newfmt.h" @@ -34,7 +34,7 @@ void *stackpointer; static void test(void) { mps_arena_t arena; - mps_pool_t poolMV, poolAMC; + mps_pool_t poolMVFF, poolAMC; mps_thr_t thread; mps_fmt_t format; @@ -67,33 +67,43 @@ static void test(void) comment("Sizes in megabytes:"); - die(mps_pool_create(&poolMV, arena, mps_class_mv(), - EXTEND_BY, MEAN_SIZE, MAX_SIZE), - "create MV pool"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_EXTEND_BY, EXTEND_BY); + MPS_ARGS_ADD(args, MPS_KEY_MEAN_SIZE, MEAN_SIZE); + cdie(mps_pool_create_k(&poolMVFF, arena, mps_class_mvff(), args), + "create MVFF pool"); + } MPS_ARGS_END(args); i = 0; - while ((r=mps_alloc(&p, poolMV, 1024*1024)) == 0) i++; + while ((r=mps_alloc(&p, poolMVFF, 1024*1024)) == 0) i++; report("refuse1", "%s", err_text(r)); report("size1", "%i", i); s1 = i; - mps_pool_destroy(poolMV); + mps_pool_destroy(poolMVFF); - die(mps_pool_create(&poolMV, arena, mps_class_mv(), - EXTEND_BY, MEAN_SIZE, MAX_SIZE), - "create MV pool"); + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_EXTEND_BY, EXTEND_BY); + MPS_ARGS_ADD(args, MPS_KEY_MEAN_SIZE, MEAN_SIZE); + cdie(mps_pool_create_k(&poolMVFF, arena, mps_class_mvff(), args), + "create MVFF pool"); + } MPS_ARGS_END(args); i = 0; - while ((r=mps_alloc(&p, poolMV, 1024*1024)) == 0) i++; + while ((r=mps_alloc(&p, poolMVFF, 1024*1024)) == 0) i++; report("refuse2", "%s", err_text(r)); report("size2", "%i", i); s2 = i; - mps_pool_destroy(poolMV); + mps_pool_destroy(poolMVFF); a = allocdumb(ap, 1024*1024*30); /* allocate 30 M object */ - die(mps_pool_create(&poolMV, arena, mps_class_mv(), - EXTEND_BY, MEAN_SIZE, MAX_SIZE), - "create MV pool"); + + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_EXTEND_BY, EXTEND_BY); + MPS_ARGS_ADD(args, MPS_KEY_MEAN_SIZE, MEAN_SIZE); + cdie(mps_pool_create_k(&poolMVFF, arena, mps_class_mvff(), args), + "create MVFF pool"); + } MPS_ARGS_END(args); i=0; - while ((r=mps_alloc(&p, poolMV, 1024*1024)) == 0) i++; + while ((r=mps_alloc(&p, poolMVFF, 1024*1024)) == 0) i++; report("refuse3", "%s", err_text(r)); report("size3", "%i", i); s3 = i; @@ -102,12 +112,12 @@ static void test(void) report("diff23", "%i", s2-s3); for(i = 0; i < 10; i++) { - r = mps_alloc(&p, poolMV, 1024*1024); + r = mps_alloc(&p, poolMVFF, 1024*1024); report("refuse4", "%s", err_text(r)); } mps_arena_park(arena); - mps_pool_destroy(poolMV); + mps_pool_destroy(poolMVFF); mps_ap_destroy(ap); diff --git a/mps/test/function/232.c b/mps/test/function/232.c new file mode 100644 index 00000000000..ff16289fc1b --- /dev/null +++ b/mps/test/function/232.c @@ -0,0 +1,59 @@ +/* +TEST_HEADER + id = $Id$ + summary = test arena extension and compaction + language = c + link = testlib.o + parameters = CHUNKSIZE=1024*1024 ITERATIONS=100 +END_HEADER +*/ + +#include "mpm.h" +#include "mpscmvff.h" +#include "testlib.h" + +static void check_chunks(mps_arena_t arena, unsigned expected) +{ + unsigned chunks = (unsigned)RingLength(ArenaChunkRing((Arena)arena)); + asserts(chunks == expected, "expected %u chunks, got %u", expected, chunks); +} + +static void test(void) +{ + mps_arena_t arena; + mps_pool_t pool; + mps_addr_t block[ITERATIONS]; + unsigned i; + + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, CHUNKSIZE); + die(mps_arena_create_k(&arena, mps_arena_class_vm(), args), "arena_create"); + } MPS_ARGS_END(args); + + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_SPARE, 0); + die(mps_pool_create_k(&pool, arena, mps_class_mvff(), args), "pool_create"); + } MPS_ARGS_END(args); + check_chunks(arena, 1); + + for (i = 0; i < ITERATIONS; ++i) { + die(mps_alloc(&block[i], pool, CHUNKSIZE), "mps_alloc"); + check_chunks(arena, i + 2); + } + + for (i = ITERATIONS; i > 0; --i) { + mps_free(pool, block[i - 1], CHUNKSIZE); + mps_arena_collect(arena); /* ensure ArenaCompact is called */ + check_chunks(arena, i); + } + + mps_pool_destroy(pool); + mps_arena_destroy(arena); +} + +int main(void) +{ + easy_tramp(test); + pass(); + return 0; +} diff --git a/mps/test/function/233.c b/mps/test/function/233.c new file mode 100644 index 00000000000..e0fb4296910 --- /dev/null +++ b/mps/test/function/233.c @@ -0,0 +1,47 @@ +/* +TEST_HEADER + id = $Id$ + summary = MFS can allocate when UNIT_SIZE == EXTEND_BY + language = c + link = testlib.o +END_HEADER +*/ + +#include "mpm.h" +#include "mpscmfs.h" +#include "testlib.h" + +static void test(void) +{ + size_t i; + for (i = 0; i < 20; ++i) { + size_t unitSize = (size_t)1 << i; + mps_arena_t arena; + mps_pool_t pool; + mps_addr_t p; + + MPS_ARGS_BEGIN(args) { + die(mps_arena_create_k(&arena, mps_arena_class_vm(), args), + "arena_create"); + } MPS_ARGS_END(args); + + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_MFS_UNIT_SIZE, unitSize); + MPS_ARGS_ADD(args, MPS_KEY_EXTEND_BY, unitSize); + die(mps_pool_create_k(&pool, arena, mps_class_mfs(), args), + "pool_create"); + } MPS_ARGS_END(args); + + die(mps_alloc(&p, pool, unitSize), "alloc"); + + mps_pool_destroy(pool); + mps_arena_destroy(arena); + } +} + +int main(void) +{ + easy_tramp(test); + pass(); + return 0; +} diff --git a/mps/test/function/234.c b/mps/test/function/234.c new file mode 100644 index 00000000000..7d160aaef1e --- /dev/null +++ b/mps/test/function/234.c @@ -0,0 +1,79 @@ +/* +TEST_HEADER + id = $Id$ + summary = empty traces (regression test for job004086) + language = c + link = testlib.o rankfmt.o +END_HEADER +*/ + +#include "mpsavm.h" +#include "mpscamc.h" +#include "mpscams.h" +#include "mpscawl.h" +#include "mpsclo.h" +#include "rankfmt.h" +#include "testlib.h" + +static void *stackpointer; + +static void test_pool(mps_pool_class_t pool_class) +{ + mps_arena_t arena; + mps_pool_t pool; + mps_thr_t thread; + mps_root_t root; + mps_fmt_t format; + mps_ap_t ap; + void *addr; + + cdie(mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none), + "create arena"); + cdie(mps_thread_reg(&thread, arena), "register thread"); + cdie(mps_root_create_thread(&root, arena, thread, stackpointer), + "create thread"); + 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, pool_class, args), "create pool"); + } MPS_ARGS_END(args); + cdie(mps_ap_create_k(&ap, pool, mps_args_none), "create ap"); + + /* First reserve on the allocation point causes the pool to create a + buffered segment but with no objects yet. */ + cdie(mps_reserve(&addr, ap, MPS_PF_ALIGN), "reserve"); + + /* Create a trace and condemn the world, but discover that no + objects were condemned, so abort the trace. */ + mps_arena_collect(arena); + + /* Collect again to ensure that aborting the trace after condemning + didn't violate the trace invariants. */ + mps_arena_collect(arena); + + asserts(mps_commit(ap, &addr, MPS_PF_ALIGN), "commit"); + + mps_ap_destroy(ap); + mps_pool_destroy(pool); + mps_fmt_destroy(format); + mps_root_destroy(root); + mps_thread_dereg(thread); + mps_arena_destroy(arena); +} + +static void test(void) +{ + test_pool(mps_class_amc()); + test_pool(mps_class_ams()); + test_pool(mps_class_awl()); + test_pool(mps_class_lo()); +} + +int main(void) +{ + void *m; + stackpointer = &m; /* hack to get stack pointer */ + easy_tramp(test); + pass(); + return 0; +} diff --git a/mps/test/function/25.c b/mps/test/function/25.c index 5e4b2e37d71..f6450b95153 100644 --- a/mps/test/function/25.c +++ b/mps/test/function/25.c @@ -4,6 +4,7 @@ TEST_HEADER summary = string twiddling with an LO pool language = c link = lofmt.o testlib.o + parameters = ITERATIONS=10000 END_HEADER */ @@ -16,8 +17,6 @@ END_HEADER void *stackpointer; mps_ap_t ap; -#define MAXLEN 1000000; - static locell *string_ch(char* x) { size_t len; @@ -89,7 +88,7 @@ static void test(void) { (void)string_ch("Wibble wobble foo"); (void)string_ch("Ba "); - for (i=0; i<10000; i++) { + for (i=0; i<ITERATIONS; i++) { a = conc(string_ch("B"), a); (void)conc(string_ch("Hello there"), string_ch(" folks!")); } diff --git a/mps/test/function/26.c b/mps/test/function/26.c index fc738f90733..5bfba0b6678 100644 --- a/mps/test/function/26.c +++ b/mps/test/function/26.c @@ -10,7 +10,7 @@ END_HEADER */ #include "testlib.h" -#include "mpscmv.h" +#include "mpscmvff.h" mps_arena_t arena; mps_pool_t pool; @@ -19,8 +19,7 @@ mps_addr_t q; static mps_res_t trysize(size_t try) { mps_res_t res; - die(mps_pool_create(&pool, arena, mps_class_mv(), - 1024*32, 1024*16, 1024*256), "pool"); + cdie(mps_pool_create_k(&pool, arena, mps_class_mvff(), mps_args_none), "pool"); comment("Trying %x", try); diff --git a/mps/test/function/29.c b/mps/test/function/29.c index 32ac7c841a4..94ef6007d02 100644 --- a/mps/test/function/29.c +++ b/mps/test/function/29.c @@ -4,6 +4,7 @@ TEST_HEADER summary = big allocation with an LO pool language = c link = lofmt.o testlib.o + parameters = ITERATIONS=10000 END_HEADER */ @@ -15,8 +16,6 @@ END_HEADER void *stackpointer; mps_ap_t ap; -#define MAXLEN 1000000; - static locell *string_ch(char* x) { size_t len; @@ -88,7 +87,7 @@ static void test(void) { (void)string_ch("Wibble wobble foo"); (void)string_ch("Ba "); - for (i=0; i<10000; i++) { + for (i=0; i<ITERATIONS; i++) { a = conc(string_ch("B"), a); (void)conc(string_ch("Hello there"), string_ch(" folks!")); (void)alloclo(ap, 0x4000); diff --git a/mps/test/function/30.c b/mps/test/function/30.c index 583429d8bc2..416c41323ff 100644 --- a/mps/test/function/30.c +++ b/mps/test/function/30.c @@ -4,6 +4,7 @@ TEST_HEADER summary = test my format for checking the graph language = c link = testlib.o awlfmt.o + parameters = ITERATIONS=50 END_HEADER */ @@ -48,8 +49,8 @@ static void test(void) cdie(mps_ap_create(&ap, pool, mps_rank_exact()), "create ap"); - for (j = 1; j < 100; j++) { - comment("%i of 100.", j); + for (j = 1; j <= ITERATIONS; j++) { + comment("%i of %i.", j, ITERATIONS); UC; a = allocone(ap, 5, 1); b = a; @@ -59,7 +60,7 @@ static void test(void) f = a; g = a; - for (i = 1; i < 100; i++) { + for (i = 0; i < 100; i++) { UC; c = allocone(ap, 1000, 1); if (ranint(8) == 0) d = c; diff --git a/mps/test/function/35.c b/mps/test/function/35.c index 42def83780a..1e441c5ec58 100644 --- a/mps/test/function/35.c +++ b/mps/test/function/35.c @@ -4,6 +4,7 @@ TEST_HEADER summary = provoke segsummary assertion (request.dylan.170450) language = c link = testlib.o awlfmt.o + parameters = ITERATIONS=10 END_HEADER */ @@ -67,9 +68,9 @@ static void test(void) mps_ap_create(&ap, pool, mps_rank_exact()), "create ap"); - for (j=1; j<100; j++) + for (j=1; j<=ITERATIONS; j++) { - comment("%i of 100.", j); + comment("%i of %i.", j, ITERATIONS); UC; *a = allocone(ap, 5, 1); *b = *a; diff --git a/mps/test/function/36.c b/mps/test/function/36.c index 609d9f9210b..c3f0de5961e 100644 --- a/mps/test/function/36.c +++ b/mps/test/function/36.c @@ -40,10 +40,6 @@ static void test(void) int j; int k,z; - alloccomments = 1; - skipcomments = 1; - formatcomments = 1; - cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); @@ -76,7 +72,7 @@ static void test(void) for(i=0; i<10000; i++) { j = ranint(100); - k = 5 + ranint(500); + k = 5 + ranint(100); if (ranint(2)==1) { apran = apawl; } else { diff --git a/mps/test/function/38.c b/mps/test/function/38.c index e3512864442..4c8b994c31f 100644 --- a/mps/test/function/38.c +++ b/mps/test/function/38.c @@ -9,7 +9,7 @@ END_HEADER #include "testlib.h" #include "mpscawl.h" -#include "mpscmv.h" +#include "mpscmvff.h" #include "mpscamc.h" #include "mpsavm.h" #include "rankfmt.h" @@ -52,7 +52,7 @@ static void checklds(void) static void test(void) { - mps_pool_t poolmv, poolawl, poolamc; + mps_pool_t poolmvff, poolawl, poolamc; mps_thr_t thread; mps_root_t root0, root1, root2; @@ -91,8 +91,8 @@ static void test(void) cdie(mps_pool_create(&poolawl, arena, mps_class_awl(), format, getassociated), "create awl pool"); - cdie(mps_pool_create(&poolmv, arena, mps_class_mv(), 0x4000, 128, 0x4000), - "create mv pool"); + cdie(mps_pool_create_k(&poolmvff, arena, mps_class_mvff(), mps_args_none), + "create MVFF pool"); cdie(mps_ap_create(&apawl, poolawl, mps_rank_exact()), "create ap"); @@ -104,7 +104,7 @@ static void test(void) for (i=0; i < MAXLDS; i++) { comment("%d", i); - mps_alloc(&p, poolmv, sizeof(mps_ld_s)); + mps_alloc(&p, poolmvff, sizeof(mps_ld_s)); a = allocone(apawl, 5, mps_rank_exact()); setref(a, 0, b); b = a; @@ -154,7 +154,7 @@ static void test(void) mps_arena_park(arena); mps_ap_destroy(apawl); mps_ap_destroy(apamc); - mps_pool_destroy(poolmv); + mps_pool_destroy(poolmvff); mps_pool_destroy(poolamc); mps_pool_destroy(poolawl); comment("Destroyed pools."); diff --git a/mps/test/function/41.c b/mps/test/function/41.c index 8fcdb372a6d..98ef3a51d46 100644 --- a/mps/test/function/41.c +++ b/mps/test/function/41.c @@ -71,7 +71,7 @@ static void test(void) b = allocone(apamc, 1, 1); - for (j=1; j<10; j++) { + for (j=1; j<=10; j++) { comment("%i of 10.", j); UC; a = allocone(apawl, 5, 1); @@ -84,10 +84,10 @@ static void test(void) f = a; g = a; - for (i=1; i<1000; i++) { + for (i=0; i<100; i++) { UC; - c = allocone(apamc, 10000, 1); - c = allocone(apawl, 10000, 1); + c = allocone(apamc, 1000, 1); + c = allocone(apawl, 1000, 1); c->data.assoc = stackpointer; if (ranint(8) == 0) d = c; if (ranint(8) == 0) e = c; diff --git a/mps/test/function/42.c b/mps/test/function/42.c index b56a61f2a09..85810297675 100644 --- a/mps/test/function/42.c +++ b/mps/test/function/42.c @@ -69,7 +69,7 @@ static void test(void) b = allocone(apamc, 1, 1); - for (j=1; j<10; j++) { + for (j=1; j<=10; j++) { comment("%i of 10.", j); UC; a = allocone(apawl, 5, 1); @@ -81,10 +81,10 @@ static void test(void) f = a; g = a; - for (i=1; i<1000; i++) { + for (i=0; i<1000; i++) { UC; - c = allocone(apamc, 100, 1); - c = allocone(apawl, 100, 1); + c = allocone(apamc, 50, 1); + c = allocone(apawl, 50, 1); if (ranint(8) == 0) d = c; if (ranint(8) == 0) e = c; if (ranint(8) == 0) f = c; diff --git a/mps/test/function/43.c b/mps/test/function/43.c index 1b955f268b7..106b7b7e10f 100644 --- a/mps/test/function/43.c +++ b/mps/test/function/43.c @@ -73,7 +73,7 @@ static void test(void) b = allocone(apamc, 1, 1); - for (j=1; j<10; j++) { + for (j=1; j<=10; j++) { comment("%i of 10.", j); UC; a = allocone(apexact, 5, 1); @@ -85,13 +85,13 @@ static void test(void) f = a; g = a; - for (i=1; i<1000; i++) { + for (i=0; i<1000; i++) { UC; - c = allocone(apamc, 1000, 0); + c = allocone(apamc, 50, 0); if (ranint(2) == 0) { - c = allocone(apweak, 100, 1); + c = allocone(apweak, 50, 1); } else { - c = allocone(apexact, 100, 1); + c = allocone(apexact, 50, 1); } if (ranint(8) == 0) d = c; if (ranint(8) == 0) e = c; diff --git a/mps/test/function/44.c b/mps/test/function/44.c index f38e36a06e0..e99717e0230 100644 --- a/mps/test/function/44.c +++ b/mps/test/function/44.c @@ -81,7 +81,7 @@ static void test(void) b = allocone(apamc, 1, 1); - for (j=1; j<10; j++) { + for (j=1; j<=10; j++) { comment("%i of 10.", j); a = allocone(apawl, 5, 1); setref(b, 0, a); @@ -91,14 +91,14 @@ static void test(void) e = a; f = a; g = a; - for (i=1; i<1000; i++) { + for (i=1; i<=1000; i++) { if (i%100 == 0) { comment(" %i", i); } if (ranint(2)) { - c = allocone(apamc, 1000, 1); + c = allocone(apamc, 100, 1); } else { - c = allocone(apawl, 1000, 1); + c = allocone(apawl, 100, 1); } if (ranint(8) == 0) d = c; if (ranint(8) == 0) e = c; @@ -129,14 +129,14 @@ static void test(void) comment("clamping..."); mps_arena_park(arena); RC; - for (i=1; i<1000; i++) { + for (i=1; i<=1000; i++) { if (i%100 == 0) { comment(" %i", i); } if (ranint(2)) { - c = allocone(apamc, 1000, 1); + c = allocone(apamc, 100, 1); } else { - c = allocone(apawl, 1000, 1); + c = allocone(apawl, 100, 1); } if (ranint(8) == 0) d = c; if (ranint(8) == 0) e = c; diff --git a/mps/test/function/45.c b/mps/test/function/45.c index 50fdc5ca07c..8001b47f362 100644 --- a/mps/test/function/45.c +++ b/mps/test/function/45.c @@ -4,6 +4,7 @@ TEST_HEADER summary = arena_collect when lots of APs are in mid-cycle language = c link = testlib.o newfmt.o + parameters = VERBOSE=0 NCELLS=100 NAPS=100 ITERATIONS=10 END_HEADER */ @@ -17,11 +18,8 @@ END_HEADER #include "newfmt.h" -#define NCELLS 100 -#define NAPS 100 #define PNULL (ranint(100)<25) #define NUMREFS (ranint(80)) -#define BLAH 0 #define genCOUNT (3) @@ -64,8 +62,8 @@ static void test(void) int nextid = 0x1000000; /* turn on comments about copying and scanning */ - formatcomments = BLAH; - fixcomments = BLAH; + formatcomments = VERBOSE; + fixcomments = VERBOSE; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); @@ -96,8 +94,8 @@ static void test(void) 0 after commit */ - for(h=0; h<100; h++) { - comment("%i of 100", h); + for(h=0; h<ITERATIONS; h++) { + comment("%i of %i", h, ITERATIONS); for(j=0; j<1000; j++) { if (j == 500) { @@ -125,11 +123,11 @@ static void test(void) p[i]->data.tag = 0xD033E2A6; p[i]->data.id = nextid; ap_state[i] = 1; - commentif(BLAH, "%i: reserve %li at %p", i, nextid, q); + commentif(VERBOSE, "%i: reserve %li at %p", i, nextid, q); nextid +=1; break; case 1: - commentif(BLAH, "%i: init %li", i, p[i]->data.id); + commentif(VERBOSE, "%i: init %li", i, p[i]->data.id); p[i]->data.tag = MCdata; p[i]->data.numrefs = nrefs[i]; p[i]->data.size = s[i]; @@ -144,22 +142,22 @@ static void test(void) p[i]->data.ref[k].addr = pobj; p[i]->data.ref[k].id = (pobj==NULL ? 0 : pobj->data.id); } - commentif(BLAH, " ref %i -> %li", k, p[i]->data.ref[k].id); + commentif(VERBOSE, " ref %i -> %li", k, p[i]->data.ref[k].id); } break; case 2: - commentif(BLAH, "%i: begin commit %li", i, p[i]->data.id); + commentif(VERBOSE, "%i: begin commit %li", i, p[i]->data.id); ambig[i] = p[i]; ap[i]->init = ap[i]->alloc; ap_state[i] = 3; break; case 3: - commentif(BLAH, "%i: end commit %li", i, p[i]->data.id); + commentif(VERBOSE, "%i: end commit %li", i, p[i]->data.id); q=p[i]; if (ap[i]->limit != 0 || mps_ap_trip(ap[i], p[i], s[i])) { l = ranint(NCELLS); setref(cells, l, q); - commentif(BLAH, "%i -> %i", i, l); + commentif(VERBOSE, "%i -> %i", i, l); } ap_state[i] = 0; ambig[i] = NULL; @@ -178,15 +176,15 @@ static void test(void) for (i=0; i<NAPS; i++) { switch (ap_state[i]) { case 1: - commentif(BLAH, "%i init", i); + commentif(VERBOSE, "%i init", i); p[i]->data.tag = MCdata; p[i]->data.numrefs = 0; p[i]->data.size = s[i]; case 2: - commentif(BLAH, "%i begin commit", i); + commentif(VERBOSE, "%i begin commit", i); ap[i]->init = ap[i]->alloc; case 3: - commentif(BLAH, "% end commit", i); + commentif(VERBOSE, "%i end commit", i); (void) (ap[i]->limit != 0 || mps_ap_trip(ap[i], p[i], s[i])); } mps_ap_destroy(ap[i]); diff --git a/mps/test/function/47.c b/mps/test/function/47.c index ee7c5605c2e..d7b0822448d 100644 --- a/mps/test/function/47.c +++ b/mps/test/function/47.c @@ -9,7 +9,7 @@ END_HEADER #include "testlib.h" #include "mpscawl.h" -#include "mpscmv.h" +#include "mpscmvff.h" #include "mpscamc.h" #include "mpsavm.h" #include "rankfmt.h" @@ -22,7 +22,7 @@ void *stackpointer; static void test(void) { mps_arena_t arena; - mps_pool_t poolmv, poolawl; + mps_pool_t poolmvff, poolawl; mps_thr_t thread; mps_root_t root0, root1; @@ -54,8 +54,8 @@ static void test(void) { cdie(mps_pool_create(&poolawl, arena, mps_class_awl(), format, getassociated), "create awl pool"); - cdie(mps_pool_create(&poolmv, arena, mps_class_mv(), 0x4000, 128, 0x4000), - "create mv pool"); + cdie(mps_pool_create_k(&poolmvff, arena, mps_class_mvff(), mps_args_none), + "create MVFF pool"); cdie(mps_ap_create(&apawl, poolawl, mps_rank_exact()), "create ap"); @@ -64,7 +64,7 @@ static void test(void) { for (i=0; i < MAXLDS; i++) { comment("%d", i); - mps_alloc(&p, poolmv, sizeof(mps_ld_s)); + mps_alloc(&p, poolmvff, sizeof(mps_ld_s)); a = allocone(apawl, 5, mps_rank_exact()); setref(a, 0, b); b = a; @@ -91,7 +91,7 @@ static void test(void) { mps_ap_destroy(apawl); comment("Destroyed ap."); - mps_pool_destroy(poolmv); + mps_pool_destroy(poolmvff); mps_pool_destroy(poolawl); comment("Destroyed pools."); diff --git a/mps/test/function/48.c b/mps/test/function/48.c index 3343684fdbf..cdc1c66737f 100644 --- a/mps/test/function/48.c +++ b/mps/test/function/48.c @@ -41,7 +41,7 @@ static void test(void) RC; - cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), + cdie(mps_arena_create(&arena, mps_arena_class_vm(), 1ul<<30), "create arena"); die(mps_thread_reg(&thread, arena), "register thread"); @@ -73,7 +73,7 @@ static void test(void) b = allocone(apamc, 1, 1); - for (j=1; j<10; j++) { + for (j=1; j<=10; j++) { comment("%i of 10.", j); UC; a = allocone(apawl, 5, mps_rank_exact()); @@ -85,14 +85,14 @@ static void test(void) f = a; g = a; - for (i=1; i<1000; i++) { + for (i=1; i<=1000; i++) { UC; if (ranint(3) == 0) { - c = allocone(apawl, 500, mps_rank_exact()); + c = allocone(apawl, 100, mps_rank_exact()); } else if (ranint(2) == 0) { - c = allocone(apweak, 500, mps_rank_weak()); + c = allocone(apweak, 100, mps_rank_weak()); } else { - c = allocone(apamc, 500, mps_rank_exact()); + c = allocone(apamc, 100, mps_rank_exact()); } if (ranint(8) == 0) d = c; if (ranint(8) == 0) e = c; diff --git a/mps/test/function/49.c b/mps/test/function/49.c index 27864e380e9..b7db41d475d 100644 --- a/mps/test/function/49.c +++ b/mps/test/function/49.c @@ -135,7 +135,7 @@ static void test(void) long int j; - cdie(mps_arena_create(&arena, mps_arena_class_vm(), (size_t)1024*1024*30), + cdie(mps_arena_create_k(&arena, mps_arena_class_vm(), mps_args_none), "create arena"); cdie(mps_thread_reg(&thread, arena), "register thread"); @@ -217,7 +217,7 @@ static void test(void) for (j=0; j<1000; j++) { if (j % 50 == 0) - comment("%d of 1000", j); + comment("%d of 1000", j+1); a = allocone(apamc, 10000, mps_rank_exact()); mps_finalize(arena, (mps_addr_t*)&a); final_count +=1; @@ -228,7 +228,7 @@ static void test(void) comment("reregister"); for (j=0; j<500; j++) { - comment("%d of 500", j); + comment("%d of 500", j+1); qpoll(&z, FINAL_REREGISTER); } @@ -236,7 +236,7 @@ static void test(void) z = a; for (j=0; j<1000; j++) { - comment("%d of 1000", j); + comment("%d of 1000", j+1); finalpoll(&z, FINAL_QUEUE); qpoll(&z, FINAL_STORE); a = allocone(apamc, 2, mps_rank_exact()); diff --git a/mps/test/function/55.c b/mps/test/function/55.c index f34a2fb1856..a5a4d3e5f07 100644 --- a/mps/test/function/55.c +++ b/mps/test/function/55.c @@ -4,6 +4,7 @@ TEST_HEADER summary = use AMC and LO without inactive LO pool language = c link = testlib.o awlfmt.o + parameters = ITERATIONS=10 END_HEADER */ @@ -26,13 +27,13 @@ void *stackpointer; static void test(void) { mps_arena_t arena; - mps_pool_t poolamc, poolawl; + mps_pool_t poolamc, poollo; mps_thr_t thread; mps_root_t root; mps_fmt_t format; mps_chain_t chain; - mps_ap_t apamc, apawl; + mps_ap_t apamc, aplo; mycell *a, *b, *c, *d, *e, *f, *g; @@ -56,11 +57,11 @@ static void test(void) "create pool(amc)"); cdie( - mps_pool_create(&poolawl, arena, mps_class_lo(), format), + mps_pool_create(&poollo, arena, mps_class_lo(), format), "create pool"); cdie( - mps_ap_create(&apawl, poolawl, mps_rank_exact()), + mps_ap_create(&aplo, poollo, mps_rank_exact()), "create ap"); cdie( @@ -69,8 +70,8 @@ static void test(void) b = allocone(apamc, 1, 1); - for (j = 1; j < 100; j++) { - comment("%i of 100.", j); + for (j = 1; j <= ITERATIONS; j++) { + comment("%i of %i.", j, ITERATIONS); UC; a = allocone(apamc, 5, 1); b = a; @@ -80,10 +81,10 @@ static void test(void) f = a; g = a; - for (i = 1; i < 5000; i++) { + for (i = 0; i < 5000; i++) { UC; c = allocone(apamc, 20, 1); - d = allocone(apawl, 20, 1); + d = allocone(aplo, 20, 1); if (ranint(8) == 0) e = c; if (ranint(8) == 0) f = c; if (ranint(8) == 0) g = c; @@ -105,10 +106,10 @@ static void test(void) } mps_arena_park(arena); - mps_ap_destroy(apawl); + mps_ap_destroy(aplo); mps_ap_destroy(apamc); mps_pool_destroy(poolamc); - mps_pool_destroy(poolawl); + mps_pool_destroy(poollo); mps_chain_destroy(chain); mps_fmt_destroy(format); mps_root_destroy(root); diff --git a/mps/test/function/60.c b/mps/test/function/60.c index b075e0b938e..25eb6da8118 100644 --- a/mps/test/function/60.c +++ b/mps/test/function/60.c @@ -75,8 +75,8 @@ static void test(void) for (i=1; i<=1000; i++) { UC; - a = allocone(ap1, 100, 1); - b = allocone(ap2, 100, 1); + a = allocone(ap1, 50, 1); + b = allocone(ap2, 50, 1); setref(a, 0, b); setref(b, 0, a); UC; diff --git a/mps/test/function/61.c b/mps/test/function/61.c index 9ccd97d77de..37b66fa5ea3 100644 --- a/mps/test/function/61.c +++ b/mps/test/function/61.c @@ -4,6 +4,7 @@ TEST_HEADER summary = loops between two AMC pools language = c link = testlib.o awlfmt.o + parameters = ITERATIONS=20 END_HEADER */ @@ -64,8 +65,8 @@ static void test(void) mps_ap_create(&ap2, poolamc2, mps_rank_exact()), "create ap"); - for (j = 1; j < 100; j++) { - comment("%i of 100.", j); + for (j = 1; j <= ITERATIONS; j++) { + comment("%i of %i.", j, ITERATIONS); for (i = 1; i < 10000; i++) { UC; diff --git a/mps/test/function/66.c b/mps/test/function/66.c index d395dbb3637..46902f8397c 100644 --- a/mps/test/function/66.c +++ b/mps/test/function/66.c @@ -9,7 +9,7 @@ END_HEADER #include "testlib.h" #include "mpscawl.h" -#include "mpscmv.h" +#include "mpscmvff.h" #include "mpscamc.h" #include "rankfmt.h" @@ -44,7 +44,7 @@ static void checklds(void) { } static void test(void) { - mps_pool_t poolmv, poolawl, poolamc; + mps_pool_t poolmvff, poolawl, poolamc; mps_thr_t thread; mps_root_t root0, root1, root2; @@ -89,9 +89,8 @@ static void test(void) { mps_pool_create(&poolawl, arena, mps_class_awl(), format, getassociated), "create awl pool"); - cdie( - mps_pool_create(&poolmv, arena, mps_class_mv(), 0x4000, 128, 0x4000), - "create mv pool"); + cdie(mps_pool_create_k(&poolmvff, arena, mps_class_mvff(), mps_args_none), + "create MVFF pool"); cdie( mps_ap_create(&apawl, poolawl, mps_rank_exact()), @@ -115,7 +114,7 @@ static void test(void) { for (i=0; i < MAXLDS; i++) { comment("%d", i); - mps_alloc(&p, poolmv, sizeof(mps_ld_s)); + mps_alloc(&p, poolmvff, sizeof(mps_ld_s)); a = allocone(apawl, 5, mps_rank_exact()); setref(a, 0, b); b = a; @@ -160,7 +159,7 @@ static void test(void) { mps_ap_destroy(apamc); comment("Destroyed aps."); - mps_pool_destroy(poolmv); + mps_pool_destroy(poolmvff); mps_pool_destroy(poolamc); mps_pool_destroy(poolawl); comment("Destroyed pools."); diff --git a/mps/test/function/69.c b/mps/test/function/69.c index bbc758b7f42..23d151b01aa 100644 --- a/mps/test/function/69.c +++ b/mps/test/function/69.c @@ -26,8 +26,7 @@ mps_arena_t arena; static void test(void) { mps_pool_t pool; - mps_thr_t thread; - mps_root_t root; + mps_root_t rootA, rootB; mps_fmt_t format; mps_chain_t chain; @@ -42,11 +41,12 @@ static void test(void) { cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); + mps_arena_park(arena); - 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_root_create_area(&rootA, arena, mps_rank_ambig(), (mps_rm_t)0, + &a, &a + 1, mps_scan_area, NULL), "create root"); + die(mps_root_create_area(&rootB, arena, mps_rank_ambig(), (mps_rm_t)0, + &b, &b + 1, mps_scan_area, NULL), "create root"); die(mps_fmt_create_A(&format, arena, &fmtA), "create format"); cdie(mps_chain_create(&chain, arena, genCOUNT, testChain), "chain_create"); @@ -95,12 +95,12 @@ static void test(void) { mps_message_discard(arena, message); mps_arena_park(arena); - mps_root_destroy(root); + mps_root_destroy(rootA); + mps_root_destroy(rootB); mps_ap_destroy(ap); mps_pool_destroy(pool); mps_chain_destroy(chain); mps_fmt_destroy(format); - mps_thread_dereg(thread); mps_arena_destroy(arena); comment("Destroyed arena."); } diff --git a/mps/test/function/7.c b/mps/test/function/7.c index a98c36df9d7..583ca3e6f4b 100644 --- a/mps/test/function/7.c +++ b/mps/test/function/7.c @@ -1,9 +1,10 @@ /* TEST_HEADER id = $Id$ - summary = create 1000 spaces and destroy each one just after the next is created + summary = create many arenas and destroy each one just after the next is created language = c link = testlib.o + parameters = ARENAS=1000 END_HEADER */ @@ -16,15 +17,14 @@ static void test(void) int p; - die(mps_arena_create(&arena1, mps_arena_class_vm(), mmqaArenaSIZE), "create"); - - for (p=0; p<1000; p++) + for (p=0; p<ARENAS; p++) { - die(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create"); - comment("%i", p); - mps_arena_destroy(arena1); + die(mps_arena_create(&arena, mps_arena_class_vm(), mps_args_none), "create"); + if (p > 0) + mps_arena_destroy(arena1); arena1=arena; } + mps_arena_destroy(arena1); } int main(void) diff --git a/mps/test/function/72.c b/mps/test/function/72.c index 6a761742cc2..c1d3c158f2d 100644 --- a/mps/test/function/72.c +++ b/mps/test/function/72.c @@ -15,35 +15,33 @@ END_HEADER #include "mpscamc.h" #include "exfmt.h" +void *stackpointer; + #define genCOUNT (3) static mps_gen_param_s testChain[genCOUNT] = { { 6000, 0.90 }, { 8000, 0.65 }, { 16000, 0.50 } }; -void *stackpointer; -mycell *z; - static void test(void) { mps_arena_t arena; mps_pool_t pool; - mps_thr_t thread; mps_root_t root; mps_chain_t chain; mps_fmt_t format; mps_ap_t ap; - mycell *a, *b; + mycell *a[3], *bad; + int i; cdie(mps_arena_create(&arena, mps_arena_class_vm(), mmqaArenaSIZE), "create arena"); - cdie(mps_thread_reg(&thread, arena), "register thread"); + /* Clamp the arena so that we can be sure that objects don't move. */ + mps_arena_clamp(arena); - cdie( - mps_root_create_reg(&root, arena, mps_rank_ambig(), 0, thread, - mps_stack_scan_ambig, stackpointer, 0), - "create root"); + cdie(mps_root_create_area(&root, arena, mps_rank_exact(), 0, &a[0], &a[3], + mps_scan_area, NULL), "create area"); cdie( mps_fmt_create_A(&format, arena, &fmtA), @@ -59,22 +57,27 @@ static void test(void) mps_ap_create(&ap, pool, mps_rank_exact()), "create ap"); - a = allocone(ap, 1024, 1); - z = a; - - b = allocone(ap, 1024, 1); - setref(b, 0, a); - - a = allocdumb(ap, 1024*64, 1); - a = allocdumb(ap, 1024*64, 1); + /* Remember the first allocation at a[0] (which is a root) and also + * at bad (which is not reachable). */ + bad = a[0] = a[1] = allocone(ap, 1, 1); + for (i = 0; i < 1000; ++i) { + a[2] = allocone(ap, 1, 1); + setref(a[2], 0, a[1]); + a[1] = a[2]; + } + /* The first collection will cause a[0] to move, but because bad + * isn't scanned it doesn't get updated, and ends up pointing to + * oldspace. */ comment("Collecting..."); mps_arena_collect(arena); - asserts(z != a, "Didn't move!"); + asserts(bad != a[0], "Didn't move!"); + /* Write the bad pointer into a scannable part of the heap. The MPS + * should spot this when it collects. Note that we can't use setref + * here because we need to bypass the check. */ comment("Writing bad pointer..."); - - b->data.ref[0].addr = z; + a[0]->data.ref[0].addr = bad; mps_arena_collect(arena); comment("Bad pointer not spotted in collection"); @@ -96,9 +99,6 @@ static void test(void) mps_root_destroy(root); comment("Destroyed root."); - mps_thread_dereg(thread); - comment("Deregistered thread."); - mps_arena_destroy(arena); comment("Destroyed arena."); diff --git a/mps/test/function/77.c b/mps/test/function/77.c index f57ea4bc2f6..ac8cfbbedd0 100644 --- a/mps/test/function/77.c +++ b/mps/test/function/77.c @@ -79,7 +79,7 @@ static void test(void) f = a; g = a; - for (i=1; i<5000; i++) { + for (i=1; i<=3000; i++) { c = allocone(apamc, 20, mps_rank_exact()); d = allocone(apawl, 20, mps_rank_exact()); if (ranint(8) == 0) e = c; diff --git a/mps/test/function/78.c b/mps/test/function/78.c index 331c0f2b2be..8c53f0035fb 100644 --- a/mps/test/function/78.c +++ b/mps/test/function/78.c @@ -4,6 +4,7 @@ TEST_HEADER summary = AMC and LO performance language = c link = testlib.o fastfmt.o + parameters = ITERATIONS=10 END_HEADER */ @@ -27,13 +28,13 @@ void *stackpointer; static void test(void) { mps_arena_t arena; - mps_pool_t poolamc, poolawl; + mps_pool_t poolamc, poollo; mps_thr_t thread; mps_root_t root, root1; mps_fmt_t format; mps_chain_t chain; - mps_ap_t apamc, apawl; + mps_ap_t apamc, aplo; mycell *a, *b, *c, *d, *e, *f, *g; @@ -59,11 +60,11 @@ static void test(void) "create pool(amc)"); cdie( - mps_pool_create(&poolawl, arena, mps_class_lo(), format), + mps_pool_create(&poollo, arena, mps_class_lo(), format), "create pool"); cdie( - mps_ap_create(&apawl, poolawl, mps_rank_exact()), + mps_ap_create(&aplo, poollo, mps_rank_exact()), "create ap"); cdie( @@ -72,8 +73,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 <= ITERATIONS; j++) { + comment("%i of %i.", j, ITERATIONS); a = allocone(apamc, 5, mps_rank_exact()); b = a; c = a; @@ -82,9 +83,9 @@ static void test(void) f = a; g = a; - for (i = 1; i < 5000; i++) { + for (i = 0; i < 5000; i++) { c = allocone(apamc, 20, mps_rank_exact()); - d = allocone(apawl, 20, mps_rank_exact()); + d = allocone(aplo, 20, mps_rank_exact()); if (ranint(8) == 0) e = c; if (ranint(8) == 0) f = c; if (ranint(8) == 0) g = c; @@ -98,10 +99,10 @@ static void test(void) } mps_arena_park(arena); - mps_ap_destroy(apawl); + mps_ap_destroy(aplo); mps_ap_destroy(apamc); mps_pool_destroy(poolamc); - mps_pool_destroy(poolawl); + mps_pool_destroy(poollo); mps_chain_destroy(chain); mps_fmt_destroy(format); mps_root_destroy(root); diff --git a/mps/test/function/8.c b/mps/test/function/8.c index 1b54b608170..f4ba9c9056b 100644 --- a/mps/test/function/8.c +++ b/mps/test/function/8.c @@ -1,12 +1,12 @@ /* TEST_HEADER id = $Id$ - summary = create arenas until an error results, see if it leaks at failure + summary = check that failed arena creation doesn't leak language = c link = testlib.o + parameters = ARENAS=10 OUTPUT_SPEC - arena > 10 - arena_tight = 129 + arena >= 10 END_HEADER */ @@ -14,45 +14,26 @@ END_HEADER #include "mpsavm.h" -#define minArenaSIZE ((size_t)(130 * 1024)) - - static void test(void) { - mps_arena_t arena, previousArena = NULL; + mps_arena_t arena; mps_res_t res; - size_t size = (size_t)(1024*1024*10L); int p = 0, i; - /* make sure you can create at least 10 */ - while ((res = mps_arena_create(&arena, mps_arena_class_vm(), size)) + /* fill address space with small arenas */ + while ((res = mps_arena_create(&arena, mps_arena_class_vm(), 1)) == MPS_RES_OK) { p++; - report("arena", "%i", p); } - asserts(res == MPS_RES_RESOURCE, "wrong error loop"); - /* fill address space with arenas */ - while (size > 2 * minArenaSIZE) { - size /= 2; - res = mps_arena_create(&arena, mps_arena_class_vm(), size); - asserts(res == MPS_RES_OK || res == MPS_RES_RESOURCE, "error fill"); - if (res == MPS_RES_OK) p++; - } - report("arena2", "%i", p); - report("size", "%i", size); - /* there could still be holes, fill some more */ - while ((res = mps_arena_create(&arena, mps_arena_class_vm(), minArenaSIZE)) - == MPS_RES_OK) { - p++; previousArena = arena; - } - mps_arena_destroy(previousArena); - report("arena3", "%i", p); - /* test that you can create and fail without leaking */ - for (i = 0; i < minArenaSIZE / 1024; ++i) { - res = mps_arena_create(&arena, mps_arena_class_vm(), (size_t)(1024*1024*10L)); + report("arena", "%d", p); + /* destroy one small arena */ + mps_arena_destroy(arena); + for (i = 0; i < ARENAS; ++i) { + /* there isn't enough space for a large arena */ + res = mps_arena_create(&arena, mps_arena_class_vm(), (size_t)1 << 20); asserts(res == MPS_RES_RESOURCE, "error leak"); - die(mps_arena_create(&arena, mps_arena_class_vm(), minArenaSIZE), "leak"); - report("arena_tight", "%i", i); + /* but destroying one small arena makes room for another */ + die(mps_arena_create(&arena, mps_arena_class_vm(), 1), "leak"); mps_arena_destroy(arena); } } @@ -60,6 +41,12 @@ static void test(void) int main(void) { - easy_tramp(test); + if (MPS_WORD_WIDTH <= 32) { + easy_tramp(test); + } else { + /* Can't exhaust 64-bit address space by allocating, so fake a pass. */ + report("arena", "%d", ARENAS); + } + pass(); return 0; } diff --git a/mps/test/function/96.c b/mps/test/function/96.c index 6504674cf41..287e49459dd 100644 --- a/mps/test/function/96.c +++ b/mps/test/function/96.c @@ -1,7 +1,7 @@ /* TEST_HEADER id = $Id$ - summary = low memory tests with AMC (and using MV) + summary = low memory tests with AMC (and using MVFF) language = c link = testlib.o rankfmt.o END_HEADER @@ -9,7 +9,7 @@ END_HEADER #include "testlib.h" #include "mpscamc.h" -#include "mpscmv.h" +#include "mpscmvff.h" #include "mpsavm.h" #include "rankfmt.h" @@ -22,7 +22,7 @@ static mps_gen_param_s testChain[genCOUNT] = { void *stackpointer; -mps_pool_t poolmv; +mps_pool_t poolmvff; mps_arena_t arena; @@ -30,26 +30,18 @@ static void fillup(void) { size_t size; mps_addr_t a; - char *b; - 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) { - for(b=a; b<(char *)a+size; b++) { - *b = 97; - } - } - size = size / 2; - } + cdie(mps_pool_create_k(&poolmvff, arena, mps_class_mvff(), mps_args_none), + "create MVFF pool"); + for (size=1024ul*1024ul; size >= 4096ul; size /= 2) + while (mps_alloc(&a, poolmvff, size)==MPS_RES_OK) + ; } static void empty(void) { - mps_pool_destroy(poolmv); + mps_pool_destroy(poolmvff); } @@ -115,10 +107,10 @@ static void test(void) empty(); - for (j=0; j<1000*1024; j++) { - res=allocrdumb(&a, ap, 1024, mps_rank_exact()); + for (j=0; j<1000; j++) { + res=allocrdumb(&a, ap, 1024*1024, mps_rank_exact()); if (res == MPS_RES_OK) { - if (j % 100000 == 0) { + if (j % 100 == 0) { comment("%i ok", j); } } else { diff --git a/mps/test/function/97.c b/mps/test/function/97.c index 3e5f3b8d30f..1ccb6996483 100644 --- a/mps/test/function/97.c +++ b/mps/test/function/97.c @@ -4,6 +4,7 @@ TEST_HEADER summary = test of mps_arena_formatted_objects_walk language = c link = testlib.o rankfmt.o + parameters = VERBOSE=0 END_HEADER some kinds of errors that could occur in the walker: @@ -98,7 +99,7 @@ static void stepper(mps_addr_t addr, mps_fmt_t fmt, mps_pool_t pool, appcount += 1; asserts(a->data.checkedflag != newstamp, "III/IV. step on object again at %p", a); - commentif(a->data.checkedflag != oldstamp, + commentif(VERBOSE && a->data.checkedflag != oldstamp, "*. step on unreachable object at %p", a); a->data.checkedflag = newstamp; } else { @@ -179,7 +180,7 @@ static void test(void) comment("%i of 100", j); - for (i=0; i<10000; i++) { + for (i=0; i<1000; i++) { k = ranint(4); addr = &a[k]; die(allocrdumb(addr, aplo, 64, mps_rank_exact()), "alloc failed"); diff --git a/mps/test/function/98.c b/mps/test/function/98.c index c982606990f..68704bc3ad5 100644 --- a/mps/test/function/98.c +++ b/mps/test/function/98.c @@ -1,11 +1,12 @@ /* TEST_HEADER id = $Id$ - summary = create arenas at once until an error results! + summary = fill address space with arenas until an error results! language = c link = testlib.o + parameters = ARENAS=10 OUTPUT_SPEC - arena > 10 + arena >= 10 END_HEADER */ @@ -14,22 +15,30 @@ END_HEADER static void test(void) { - mps_arena_t arena; + mps_arena_t arena; + mps_res_t res; + int p; - int p; - - p=0; - - while (1) - { - die(mps_arena_create(&arena, mps_arena_class_vm(), (size_t) (1024*1024*10)), "create"); - p = p+1; + for (p = 0;; ++p) { + MPS_ARGS_BEGIN(args) { + MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, 1); + res = mps_arena_create_k(&arena, mps_arena_class_vm(), args); + } MPS_ARGS_END(args); + if (res != MPS_RES_OK) + break; + } + asserts(res == MPS_RES_RESOURCE, "resource"); report("arena", "%i", p); - } } int main(void) { - easy_tramp(test); - return 0; + if (MPS_WORD_WIDTH <= 32) { + easy_tramp(test); + } else { + /* Can't exhaust 64-bit address space by allocating, so fake a pass. */ + report("arena", "%d", ARENAS); + } + pass(); + return 0; } diff --git a/mps/test/function/99.c b/mps/test/function/99.c index 939991c1551..c776c17bcc1 100644 --- a/mps/test/function/99.c +++ b/mps/test/function/99.c @@ -69,7 +69,7 @@ static void test(void) b = allocone(apamc, 1, mps_rank_exact()); - for (j =1 ; j < 100; j++) { + for (j =1 ; j <= 100; j++) { comment("%i of 100.", j); a = allocone(apamc, 5, mps_rank_exact()); b = a; @@ -79,7 +79,7 @@ static void test(void) f = a; g = a; - for (i = 1; i < 5000; i++) { + for (i = 0; i < 5000; i++) { c = allocone(apamc, 20, mps_rank_exact()); d = allocone(apamcz, 20, mps_rank_exact()); if (ranint(8) == 0) e = c; diff --git a/mps/test/misc/1.c b/mps/test/misc/1.c index cf955c68cec..a8477ca1f62 100644 --- a/mps/test/misc/1.c +++ b/mps/test/misc/1.c @@ -5,7 +5,7 @@ TEST_HEADER language = c link = testlib.o OUTPUT_SPEC - memoryerror = true + abort = true END_HEADER */ diff --git a/mps/test/misc/2.c b/mps/test/misc/2.c index a6ff666788b..7686fa9fb24 100644 --- a/mps/test/misc/2.c +++ b/mps/test/misc/2.c @@ -6,7 +6,7 @@ TEST_HEADER link = testlib.o parameters = NUM=1 OUTPUT_SPEC - memoryerror = true + abort = true END_HEADER */ diff --git a/mps/test/test/qa b/mps/test/test/qa index a4a40f26451..c17d3e1dca6 100644 --- a/mps/test/test/qa +++ b/mps/test/test/qa @@ -71,6 +71,7 @@ unless (-e "$script_dir/commands/".$qa_command) { die "Unknown command '".$qa_command."' -- 'qa help' for info.\n"; } +$exitstatus = 0; do "commands/".$qa_command; if ($@) {print $@}; - +exit $exitstatus; diff --git a/mps/test/test/script/clib b/mps/test/test/script/clib index 6552a20b3b7..102f0a35e0a 100644 --- a/mps/test/test/script/clib +++ b/mps/test/test/script/clib @@ -7,6 +7,7 @@ 1; +use Cwd; use File::Path qw(rmtree); sub clib { @@ -15,6 +16,7 @@ sub clib { my $tlobj; &objpurge(); + &mpslibbuild(); &scrutinize(); &logcomment("Compiling test libraries."); @@ -59,6 +61,19 @@ sub objpurge { closedir(DIR); } +# +# Build the MPS object file. +# + +sub mpslibbuild { + &logcomment("Building MPS library."); + local $dir = cwd(); + chdir($MPS_INCLUDE_DIR); + &mysystem($make_command); + chdir($dir); +} + + # # record information about environment so that when running tests # we can check the libraries are still applicable diff --git a/mps/test/test/script/commands/clib b/mps/test/test/script/commands/clib index db3bda8eb7c..8297b998907 100644 --- a/mps/test/test/script/commands/clib +++ b/mps/test/test/script/commands/clib @@ -8,15 +8,9 @@ &requiredoptions( "MPS_INCLUDE_DIR", "MPS_LINK_OBJ", -# "MPS_INTERFACE_VERSION", + "VARIETY", "PLATFORM" ); -&linkobjtimes(); - -&requiredoptions( - "MPS_PRODUCT" -); - &clib; diff --git a/mps/test/test/script/commands/debug b/mps/test/test/script/commands/debug new file mode 100644 index 00000000000..0c9bdf6b496 --- /dev/null +++ b/mps/test/test/script/commands/debug @@ -0,0 +1,16 @@ +#!/usr/bin/perl -w +# $Id$ +# +# debug a test + +&requiredoptions( + "MPS_INCLUDE_DIR", + "MPS_LINK_OBJ", + "VARIETY", + "PLATFORM", + "LOG_DIR" +); + +foreach $testfile (@ARGV) { + &debugtest($testfile); +} diff --git a/mps/test/test/script/commands/run b/mps/test/test/script/commands/run index 6633a2f16ba..3b2e63df525 100644 --- a/mps/test/test/script/commands/run +++ b/mps/test/test/script/commands/run @@ -7,8 +7,7 @@ &requiredoptions( "MPS_INCLUDE_DIR", "MPS_LINK_OBJ", -# "MPS_INTERFACE_VERSION", - "MPS_PRODUCT", + "VARIETY", "PLATFORM", "LOG_DIR" ); diff --git a/mps/test/test/script/commands/runset b/mps/test/test/script/commands/runset index d0dd02a45a2..3e579262392 100644 --- a/mps/test/test/script/commands/runset +++ b/mps/test/test/script/commands/runset @@ -7,13 +7,14 @@ &requiredoptions( "MPS_INCLUDE_DIR", "MPS_LINK_OBJ", -# "MPS_INTERFACE_VERSION", - "MPS_PRODUCT", + "VARIETY", "PLATFORM", "LOG_DIR" ); -$testset = $ARGV[0]; - -&run_testset($testset, "$LOG_DIR/summ.log", "$LOG_DIR/res.log", "$LOG_DIR/full.log"); +@LOGS = ("$LOG_DIR/summ.log", "$LOG_DIR/res.log", "$LOG_DIR/full.log"); +unlink(@LOGS); +foreach $testset (@ARGV) { + &run_testset($testset, @LOGS); +} diff --git a/mps/test/test/script/headread b/mps/test/test/script/headread index 794027763b2..2841144aa2f 100644 --- a/mps/test/test/script/headread +++ b/mps/test/test/script/headread @@ -85,6 +85,9 @@ sub readheader { } else { $testid = $test_header{"id"}; } + if (length($testid) > 70) { + $testid = substr($testid, 0, 33) . "..." . substr($testid, -33); + } if (! exists $test_header{"harness"}) { $test_header{"harness"} = "1.0"; } @@ -108,6 +111,19 @@ sub readheader { delete $spec_output{"result"}; delete $spec_rel{"result"}; } + # If the test case specifies neither completion nor failure, assume + # completion is intended. + my @expected_keys = qw(completed assert abort assert_or_abort errtext); + my $have_expected = 0; + foreach $key (@expected_keys) { + if (exists $spec_output{$key}) { + $have_expected = 1; + } + } + if (!$have_expected) { + $spec_output{"completed"} = "yes"; + $spec_rel{"completed"} = "="; + } } sub readvals { @@ -149,7 +165,7 @@ sub read_results { &debug($_); if (/^!/) { # result variable - if (/^!(\w+)\s*=\s*(.+)\s*/) { + if (/^!(\w+)\s*=\s*(.+?)\s*$/) { $real_output{$1} = $2 } else { die "Badly formatted result line in output:\n$_\n"; @@ -157,6 +173,7 @@ sub read_results { } elsif (/Abort trap|abnormal program termination|Segmentation fault/) { # abort for other reason $real_output{"abort"} = "true"; + $real_output{"assert_or_abort"} = "true"; } elsif (/^%/ || /^\s$/) { # comment or blank line } else { @@ -250,6 +267,13 @@ sub get_parmdefs { local ($var, $missing, $val); $missing = ""; %parmdefs = (); + if ($VARIETY eq "hot") { + $parmdefs{CONFIG_VAR_HOT} = 1; + } elsif ($VARIETY eq "cool") { + $parmdefs{CONFIG_VAR_COOL} = 1; + } elsif ($VARIETY eq "rash") { + $parmdefs{CONFIG_VAR_RASH} = 1; + } if (exists $test_header{"parameters"}) { foreach (split /\s+/, $test_header{"parameters"}) { ($_, $val) = split /=/, $_; diff --git a/mps/test/test/script/help/debug b/mps/test/test/script/help/debug new file mode 100644 index 00000000000..695a4779176 --- /dev/null +++ b/mps/test/test/script/help/debug @@ -0,0 +1,11 @@ +debug a test +% $Id$ + +Usage: qa debug [<options>] <testfile> + +'debug' launches a test in the debugger. The test libraries should +previously have been compiled with 'clib'; if the harness believes the +test libraries may not be up-to-date, it will give an error and ask +you to run 'clib' first. (You can force the harness to run a test with +potentially out-of-date libraries by specifying the "-danger" option +to 'debug'. This is not recommended.) diff --git a/mps/test/test/script/help/options b/mps/test/test/script/help/options index 35f3bbcc987..f22a1559658 100644 --- a/mps/test/test/script/help/options +++ b/mps/test/test/script/help/options @@ -20,19 +20,14 @@ If the option is _cumulative_, however, both will apply. What the options mean: MMQA_MPS_INCLUDE_DIR / -i - a directory where the MM header files may be found. No default. + a directory where the MPS source code may be found. Defaults to the + code directory relative to the test script. MMQA_MPS_LINK_OBJ / -l the MM libraries and plinth to link with. Separate multiple files with spaces. (Spaces in the paths will make the test harness go - wrong, unfortunately.) No default. - -MMQA_MPS_PRODUCT / -prod - You won't normally need to (and shouldn't) set this option, which - will default to "epcore" or "dylan", whichever is appropriate to - the memory manager specified in MMQA_MPS_LINK_OBJ. If however you - want to test a memory manager which predates MPS version - identification, then you must set it manually. + wrong, unfortunately.) Defaults to the appropriate file for the + platform and variety. MMQA_LOG_DIR / -g the directory in which log files should be saved. Default is @@ -50,6 +45,9 @@ MMQA_PLATFORM / -p running tests. Setting it to a value not appropriate to the machine you are using will make the test system go wrong. +MMQA_VARIETY / -v + the MPS variety to test. Defaults to cool. + MMQA_PARAMETERS / -a (cumulative) parameter settings to use for a test. These override any parameter settings specified in the test header. Examples (all equivalent): diff --git a/mps/test/test/script/help/runset b/mps/test/test/script/help/runset index 1e3c503ee37..2b2576b1179 100644 --- a/mps/test/test/script/help/runset +++ b/mps/test/test/script/help/runset @@ -1,10 +1,10 @@ -run tests from a test-set +run tests from test-sets % $Id$ -Usage: qa runset [<options>] <testsetfile> +Usage: qa runset [<options>] <testsetfile> [<testsetfile> ...] 'runset' compiles the test libraries and then runs all the -tests in a test-set. +tests in the test-sets. A test-set is a file containing a list of tests, one test per line. Blank lines or those beginning with % are ignored. diff --git a/mps/test/test/script/init b/mps/test/test/script/init index a5526d696b7..bdad439193b 100644 --- a/mps/test/test/script/init +++ b/mps/test/test/script/init @@ -13,7 +13,6 @@ sub harness_init { &set_dirs; &platform_settings; &identify; - &auto_settings; &platform_settings; } diff --git a/mps/test/test/script/options b/mps/test/test/script/options index 5ef905a09fb..2966e75baaa 100644 --- a/mps/test/test/script/options +++ b/mps/test/test/script/options @@ -14,43 +14,49 @@ sub platform_detect { if (($ENV{"OS"} || "") eq "Windows_NT") { - # it's NT, so find out what the processor is - # from a system variable - $platform_class = "nt_".$ENV{"PROCESSOR_ARCHITECTURE"}; - if ($platform_class eq "nt_") { - $platform_class = "nt_dunno"; + $platform_os = "w3"; + # See https://msdn.microsoft.com/en-us/library/aa384274.aspx + if ($ENV{"PROCESSOR_ARCHITECTURE"} eq "x86") { + $platform_ar = "i3"; + } elsif ($ENV{"PROCESSOR_ARCHITECTURE"} eq "AMD64") { + $platform_ar = "i6"; + } else { + $platform_ar = "xx"; } - $platform_phylum = "pc"; + $platform_ct = "mv"; } else { - # it's unix by default - local $os = `uname`; - local $osrel = `uname -r`; - local $processor = `uname -p`; - chomp($os); chomp($osrel); chomp($processor); - $platform_class = $os."_".$osrel."_".$processor; - $platform_class =~ s/ /_/g; - $platform_phylum = "unix"; - } - $qa_defaults{"PLATFORM"} = $platform_class."__".$platform_phylum; -} - -sub auto_settings { - unless (&getoption("MPS_PRODUCT")) { - if (&getoption("MPS_LINK_OBJ")) { - &mpslibvers(&getoption("MPS_LINK_OBJ")); - unless ($mpslibvers{"product"} eq "unknown") { - &setoption("MPS_PRODUCT", $mpslibvers{"product"}); - } + local $os = `uname -s`; + chomp($os); + if ($os eq "Darwin") { + $platform_os = "xc"; + $platform_ct = "ll"; + } elsif ($os eq "FreeBSD") { + $platform_os = "fr"; + $platform_ct = "gc"; + } elsif ($os eq "Linux") { + $platform_os = "li"; + $platform_ct = "gc"; + } else { + $platform_os = "xx"; + $platform_ct = "xx"; + } + local $processor = `uname -m`; + chomp($processor); + if ($processor eq "i386") { + $platform_ar = "i3"; + } elsif ($processor eq "x86_64") { + $platform_ar = "i6"; + } else { + $platform_ar = "xx"; } } + $qa_defaults{"PLATFORM"} = $platform_os . $platform_ar . $platform_ct; } - %qa_options = ( -# "v", "MPS_INTERFACE_VERSION", + "v", "VARIETY", "i", "MPS_INCLUDE_DIR", "l", "MPS_LINK_OBJ", - "prod", "MPS_PRODUCT", "p", "PLATFORM", "data", "DATA_DIR", "a", "PARAMETERS", @@ -73,13 +79,14 @@ sub auto_settings { # %qa_defaults = ( + "MPS_INCLUDE_DIR", $test_dir . "/../../code", "PLATFORM", "[error -- you shouldn't see this]", + "VARIETY", "cool", "DEBUG_INFO", "off", "DANGEROUS", "off", "DATA_DIR", "$test_dir/../data", "LOG_DIR", "$test_dir/log", "PARAMETERS", "", -# "MPS_INTERFACE_VERSION", "HU", "MAIL_RESULTS", "off", "MAIL_TO", "mm-qa-test-log", "MAIL_SUBJECT", "MMQA-log" diff --git a/mps/test/test/script/platform b/mps/test/test/script/platform index 88b3320db76..29c1f23c562 100644 --- a/mps/test/test/script/platform +++ b/mps/test/test/script/platform @@ -13,45 +13,41 @@ # Set lots of variables correctly, depending on the platform # (which was determined in 'options') # -# Currently, it should work correctly on NT, Solaris, Linux, MacOS X. +# Currently, it should work correctly on Windows, Linux, macOS, +# FreeBSD. # sub platform_settings { - if ($PLATFORM =~ "^nt_") { - &settings_nt(); - if ($PLATFORM =~ "^nt_x86_cap") { - &settings_nt_cap(); - } elsif ($PLATFORM =~ "^nt_x86_coff") { - &settings_nt_coff(); - } - } elsif ($PLATFORM =~ "^SunOS") { - &settings_unix(); - if ($PLATFORM =~ "sos8gp") { - &settings_gprof(); - } - } elsif ($PLATFORM =~ "^Linux") { - &settings_unix(); - &settings_linux(); - } elsif ($PLATFORM =~ "^Mac_OS_10" || $PLATFORM =~ "^Darwin_") { - &settings_unix(); - &settings_macosx(); - } elsif ($PLATFORM =~ "__unix") { - &logcomment("I don't know anything specific about $PLATFORM --"); - &logcomment("using generic unix/gcc settings."); - &settings_unix(); - } else { - die "Sorry: I don't know how to use ".$PLATFORM."\n"; - } + if ($PLATFORM =~ "^w3") { + &settings_nt(); + &settings_nt_coff(); + } elsif ($PLATFORM =~ "^li") { + &settings_unix(); + &settings_linux(); + } elsif ($PLATFORM =~ "^xc") { + &settings_unix(); + &settings_macosx(); + } elsif ($PLATFORM =~ "^fr") { + &settings_unix(); + &settings_freebsd(); + } else { + die "Sorry: I don't know how to use $PLATFORM.\n"; + } + unless (defined $MPS_LINK_OBJ && $MPS_LINK_OBJ ne "") { + $MPS_LINK_OBJ = "$MPS_INCLUDE_DIR/$link_obj"; + } } sub settings_nt { - $dirsep = "\\"; + $dirsep = "/"; + $link_obj = "$PLATFORM/$VARIETY/mps.obj"; + $make_command = "nmake /f $PLATFORM.nmk VARIETY=$VARIETY $link_obj"; + $debug_command = "vsjitdebugger"; $cc_command = "cl"; -# following line used to include /DMMQA_VERS_$MPS_INTERFACE_VERSION - $cc_opts = "/nologo /DWIN32 /D_WINDOWS /W3 /Zi /Oy- /MD /DMMQA_PROD_$MPS_PRODUCT"; + $cc_opts = "/nologo /DWIN32 /D_WINDOWS /D_CRT_SECURE_NO_WARNINGS /W3 /Zi /Oy- /MD"; $cc_link = "$obj_dir/platform.obj"; - $cc_link_opts = "/link /NODEFAULTLIB:LIBCMT /NODEFAULTLIB:LIBCMTD /NODEFAULTLIB:LIBC /NODEFAULTLIB:LIBCD /NODEFAULTLIB:MSVCRTD /DEFAULTLIB:MSVCRT /debugtype:both /pdb:none /debug:full"; + $cc_link_opts = "/link /NODEFAULTLIB:LIBCMT /NODEFAULTLIB:LIBCMTD /NODEFAULTLIB:LIBC /NODEFAULTLIB:LIBCD /NODEFAULTLIB:MSVCRTD /DEFAULTLIB:MSVCRT /debug"; $cc_include = "/I$testlib_dir /I$MPS_INCLUDE_DIR /I$obj_dir"; $cc_def = "/D"; $cc_defeq = "="; @@ -62,16 +58,16 @@ sub settings_nt { $cc_objandexe = 1; $obj_suffix = ".obj"; $try_command = ""; - $catcommand = "$script_dir/ntx86bin/cat.exe"; + $catcommand = "cat"; $comwrap = "\""; $comwrapend = "\""; $stdout_red = ">"; - $stdout_dup = "| $script_dir/ntx86bin/tee.exe"; + $stdout_dup = "| tee"; $stdin_red = "<"; $stdboth_red = ">%s 2>&1"; $quotestring = \&nt_quotestring; $platmailfile = \&nt_mailfile; - $stringscommand = "$script_dir/ntx86bin/strings.exe -20 -c"; + $stringscommand = "strings"; $preprocommand = "$cc_command /nologo $cc_preonly"; $exesuff = ".exe"; %ignored_headers = (); @@ -80,24 +76,34 @@ sub settings_nt { sub settings_nt_cap { $cc_opts = "$cc_opts /Gh"; $cc_link = "$cc_link CAP.lib"; - $cc_link_opts = "/link /NODEFAULTLIB:LIBCMT /NODEFAULTLIB:LIBCMTD /NODEFAULTLIB:LIBC /NODEFAULTLIB:LIBCD /NODEFAULTLIB:MSVCRTD /DEFAULTLIB:MSVCRT /debug:full /debugtype:both /pdb:none"; + $cc_link_opts = "/link /NODEFAULTLIB:LIBCMT /NODEFAULTLIB:LIBCMTD /NODEFAULTLIB:LIBC /NODEFAULTLIB:LIBCD /NODEFAULTLIB:MSVCRTD /DEFAULTLIB:MSVCRT /debug"; } sub settings_nt_coff { - $cc_link_opts = "/link /NODEFAULTLIB:LIBCMT /NODEFAULTLIB:LIBCMTD /NODEFAULTLIB:LIBC /NODEFAULTLIB:LIBCD /NODEFAULTLIB:MSVCRTD /DEFAULTLIB:MSVCRT /debugtype:coff /debug:full"; + $cc_link_opts = "/link /NODEFAULTLIB:LIBCMT /NODEFAULTLIB:LIBCMTD /NODEFAULTLIB:LIBC /NODEFAULTLIB:LIBCD /NODEFAULTLIB:MSVCRTD /DEFAULTLIB:MSVCRT /debug"; } sub settings_unix { $dirsep = "/"; + $link_obj = "$PLATFORM/$VARIETY/mps.o"; + $make_command = "make -B -f $PLATFORM.gmk VARIETY=$VARIETY $link_obj"; $cc_link = "$obj_dir/platform.o -lm"; $cc_link_opts = "-z muldefs"; - $cc_command = "gcc"; + if ($PLATFORM =~ /ll$/) { + $cc_command = "clang"; + $debug_command = "lldb"; + } elsif ($PLATFORM =~ /gc$/) { + $cc_command = "gcc"; + $debug_command = "gdb"; + } else { + $cc_command = "cc"; + $debug_command = "gdb"; + } $cc_opts = "-ansi -pedantic -Wall -Wstrict-prototypes ". "-Winline -Waggregate-return -Wnested-externs -Wcast-qual ". "-Wshadow -Wmissing-prototypes -Wcast-align ". - "-O -g -ggdb3 ". - "-DMMQA_PROD_$MPS_PRODUCT"; + "-O -g -ggdb3 "; $cc_include = "-I$testlib_dir -I$MPS_INCLUDE_DIR -I$obj_dir"; $cc_def = "-D"; $cc_defeq = "="; @@ -109,8 +115,12 @@ sub settings_unix { $obj_suffix = ".o"; $try_command = "sh -c "; $catcommand = "cat"; - $comwrap = "sh -c \"ulimit -c 0; "; - $comwrapend = "\""; + $comwrap = "sh -c 'ulimit -c 0; "; + # The "true" after the test program forces the test program to be run + # inside the subshell (with "Segmentation fault" messages written to + # the pipe and captured) rather than in the parent shell (with + # "Segmentation fault" messages written to the terminal and so lost). + $comwrapend = "; true'"; $stdout_red = ">"; $stdout_dup = "| tee"; $stdin_red = "<"; @@ -130,18 +140,31 @@ sub settings_gprof { sub settings_linux { - $cc_link = $cc_link . " -lpthread"; + $cc_link .= " -lpthread"; +} + + +sub settings_freebsd { + $make_command = "gmake -B -f $PLATFORM.gmk VARIETY=$VARIETY $link_obj"; + $cc_link .= " -lpthread"; } sub settings_macosx { - $cc_command = "cc"; - $cc_link = "$obj_dir/platform.o"; - $cc_link_opts =~ s/-z muldefs//; - $cc_opts =~ s/-Wstrict-prototypes//; - $cc_opts .= " -Wno-unused -Wno-missing-prototypes"; - $stdboth_red = ">&%s"; - $preprocommand = "$cc_command $cc_preonly"; + local $config = "Debug"; + if ($VARIETY eq "hot") { + $config = "Release"; + } elsif ($VARIETY eq "rash") { + $config = "Rash"; + } + $link_obj = "xc/$config/libmps.a"; + $make_command = "xcodebuild -project mps.xcodeproj -config $config -target mps"; + $debug_command = "lldb"; + $cc_command = "clang"; + $cc_link = "$obj_dir/platform.o"; + $cc_link_opts =~ s/-z muldefs//; + $cc_opts =~ s/-Wstrict-prototypes//; + $cc_opts .= " -Wno-unused -Wno-missing-prototypes"; } @@ -191,9 +214,9 @@ sub unix_mailfile { sub identify { %identify = (); - if ($PLATFORM =~ /^nt/) { + if ($PLATFORM =~ /^w3/) { &identify_nt; - } elsif ($PLATFORM =~ /__unix/) { + } elsif ($PLATFORM =~ /^(fr|li|xc)/) { &identify_unix; } $identify{"time"} = localtime; @@ -237,7 +260,7 @@ sub identify_unix { if (exists $identify{"user"}) { $identify{"user"} =~ s/\s.*//; } - &comvar("c_version", "gcc -v", "version"); + &comvar("c_version", "$cc_command -v", "version"); &comvar("OS", "uname", ""); &comvar("arch", "uname -a", ""); } diff --git a/mps/test/test/script/runtest b/mps/test/test/script/runtest index 64269c4382d..a929b6cf4a3 100644 --- a/mps/test/test/script/runtest +++ b/mps/test/test/script/runtest @@ -144,6 +144,10 @@ sub run_test { die "Don't know how to run tests in the $lang language.\n"; } + + if ($testconclusion eq "FAIL") { + $exitstatus = 1; + } } sub run_from_testset { @@ -216,6 +220,10 @@ sub run_from_testset { close(LOG_VERBOSE); &mailfile("$LOG_DIR/last.log", "$MAIL_SUBJECT $testid $testconclusion"); } + + if ($testconclusion eq "FAIL") { + $exitstatus = 1; + } } sub run_testset { @@ -228,9 +236,9 @@ sub run_testset { %testsetresults = (); - open(LOG_SUMMARY, ">".$logsummfile); - open(LOG_RESULTS, ">".$logresfile); - open(LOG_FULL, ">".$logfullfile); + open(LOG_SUMMARY, ">>".$logsummfile); + open(LOG_RESULTS, ">>".$logresfile); + open(LOG_FULL, ">>".$logfullfile); &describe_settings(LOG_SUMMARY); @LOG_FILES = (STDOUT, LOG_SUMMARY, LOG_RESULTS, LOG_FULL); &logcomment("Test set $testsetfile"); @@ -261,3 +269,35 @@ sub missingTestSymbols { return &missingSymbols(&listFileSymbols($testfile)); } + +sub debugtest { + local ($testfile,) = @_; + + &readheader($testfile, 1); + + unless (vleq($test_header{"harness"}, $HARNESS_VERSION)) { + die "This test requires version $test_header{\"harness\"} or later of the MMQA harness. +(You are using version $HARNESS_VERSION.)\n"; + } + + for $lang ($test_header{"language"}) { + if ($lang =~ /^c$/) { + unless ($DANGEROUS eq "on") { + $_ = &test_clib(); + if ($_) { + print "Warning: $_\n"; + die "-- recompile test libraries (\"qa clib\") before debugging tests.\n"; + } + } + $linkfiles = $test_header{"link"}; + $objfile = "$obj_dir/tmp_test"; + if (&compile_and_link($testfile, $objfile, $linkfiles)) { + mysystem("$debug_command $objfile") + } else { + die "compilation failed:\n$compoutput"; + } + } else { + die "Don't know how to debug tests in the $lang language.\n"; + } + } +} diff --git a/mps/test/test/script/version b/mps/test/test/script/version index 7a49f65b1ca..c2389cae686 100644 --- a/mps/test/test/script/version +++ b/mps/test/test/script/version @@ -33,4 +33,4 @@ $HARNESS_VERSION="3.5"; # 3.3.1: fix bug in reporting compiler errors when compilation # _succeeds_ # 3.4 -- Added P= (pathname equality) operator -# 3.5 -- Platform detection based on uname; Linux and Mac OS X stuff +# 3.5 -- Platform detection based on uname; Linux and macOS stuff diff --git a/mps/test/test/testlib/arg.h b/mps/test/test/testlib/arg.h index 95fa4996f3f..f2f07b81501 100644 --- a/mps/test/test/testlib/arg.h +++ b/mps/test/test/testlib/arg.h @@ -8,6 +8,7 @@ arg.h #include "testlib.h" +#undef UNALIGNED #define UNALIGNED ((mps_addr_t) (((char *) NULL) + 1)) #define MPS_RANK_MIN 0 diff --git a/mps/test/test/testlib/awlfmt.c b/mps/test/test/testlib/awlfmt.c index 352b6afcaac..0977d0de6dd 100644 --- a/mps/test/test/testlib/awlfmt.c +++ b/mps/test/test/testlib/awlfmt.c @@ -231,7 +231,7 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) p = obj->data.assoc; if (p != NULL) { commentif(fixcomments, "fix %li[assoc]", obj->data.id); - res = MPS_FIX(ss, (mps_addr_t *) &p); + res = MPS_FIX12(ss, (mps_addr_t *) &p); if (res != MPS_RES_OK) return res; if (p == NULL) { commentif(deathcomments, "fixed %li[assoc] to NULL", obj->data.id); @@ -250,7 +250,7 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) */ commentif(fixcomments, "fix %li[%i] -> %li", obj->data.id, i, obj->data.ref[i].id); - res = MPS_FIX(ss, (mps_addr_t *) &p); + res = MPS_FIX12(ss, (mps_addr_t *) &p); if (p == NULL) { commentif(deathcomments, "fixed %li[%i] to NULL", obj->data.id, i); INCCOUNTIF(obj->data.countflag, DYING_REFERENCE_COUNT); diff --git a/mps/test/test/testlib/exfmt.c b/mps/test/test/testlib/exfmt.c index 687433eed20..2b2678834a5 100644 --- a/mps/test/test/testlib/exfmt.c +++ b/mps/test/test/testlib/exfmt.c @@ -217,7 +217,7 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) p = obj->data.assoc; if (p != NULL) { commentif(fixcomments, "fix %li[assoc]", obj->data.id); - res = MPS_FIX(ss, (mps_addr_t *) &p); + res = MPS_FIX12(ss, (mps_addr_t *) &p); if (res != MPS_RES_OK) return res; if (p == NULL) { commentif(deathcomments, "fixed %li[assoc] to NULL", obj->data.id); @@ -236,7 +236,7 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) */ commentif(fixcomments, "fix %li[%i] -> %li", obj->data.id, i, obj->data.ref[i].id); - res = MPS_FIX(ss, (mps_addr_t *) &p); + res = MPS_FIX12(ss, (mps_addr_t *) &p); if (p == NULL) { commentif(deathcomments, "fixed %li[%i] to NULL", obj->data.id, i); INCCOUNTIF(obj->data.countflag, DYING_REFERENCE_COUNT); diff --git a/mps/test/test/testlib/fastfmt.c b/mps/test/test/testlib/fastfmt.c index 80beda1f4a2..dcc01d494d2 100644 --- a/mps/test/test/testlib/fastfmt.c +++ b/mps/test/test/testlib/fastfmt.c @@ -151,7 +151,7 @@ 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) { - res = MPS_FIX(ss, (mps_addr_t *) &p); + res = MPS_FIX12(ss, (mps_addr_t *) &p); if (res != MPS_RES_OK) return res; obj->data.assoc = p; } @@ -161,7 +161,7 @@ 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) { - res = MPS_FIX(ss, (mps_addr_t *) &p); + res = MPS_FIX12(ss, (mps_addr_t *) &p); if (res != MPS_RES_OK) return res; obj->data.ref[i].addr = p; } diff --git a/mps/test/test/testlib/lofmt.c b/mps/test/test/testlib/lofmt.c index 51697c00bdf..35567c8f314 100644 --- a/mps/test/test/testlib/lofmt.c +++ b/mps/test/test/testlib/lofmt.c @@ -186,7 +186,7 @@ long int getlocopycount(locell *obj) return obj->data.copycount; } -long int getlosize(locell *obj) +size_t getlosize(locell *obj) { asserts(obj->tag == LOdata, "getlosize: non-data object."); return obj->data.size - offsetof(struct lodata, data); diff --git a/mps/test/test/testlib/lofmt.h b/mps/test/test/testlib/lofmt.h index 14b9ce2a2c5..e8e2c17d02d 100644 --- a/mps/test/test/testlib/lofmt.h +++ b/mps/test/test/testlib/lofmt.h @@ -56,7 +56,7 @@ locell *alloclo(mps_ap_t ap, size_t bytes); long int getloid(locell *obj); long int getlocopycount(locell *obj); -long int getlosize(locell *obj); +size_t getlosize(locell *obj); #endif diff --git a/mps/test/test/testlib/myfmt.c b/mps/test/test/testlib/myfmt.c index 2dd95617a56..385d01fa3c2 100644 --- a/mps/test/test/testlib/myfmt.c +++ b/mps/test/test/testlib/myfmt.c @@ -5,7 +5,6 @@ myfmt.c #include "myfmt.h" #include <string.h> -#include <stdio.h> enum {MCpadsingle, MCpadmany, MCheart, MCdata}; @@ -48,8 +47,25 @@ struct mps_fmt_A_s fmtA = &mypad }; -mycell *allocone(mps_ap_t ap, mps_word_t data, - mycell *ref0, mycell *ref1, size_t size) +void fmtargs(mps_arg_s args[MPS_ARGS_MAX]) +{ + args[0].key = MPS_KEY_ALIGN; + args[0].val.align = MPS_PF_ALIGN; + args[1].key = MPS_KEY_FMT_SCAN; + args[1].val.fmt_scan = myscan; + args[2].key = MPS_KEY_FMT_SKIP; + args[2].val.fmt_skip = myskip; + args[3].key = MPS_KEY_FMT_FWD; + args[3].val.fmt_fwd = myfwd; + args[4].key = MPS_KEY_FMT_ISFWD; + args[4].val.fmt_isfwd = myisfwd; + args[5].key = MPS_KEY_FMT_PAD; + args[5].val.fmt_pad = mypad; + args[6].key = MPS_KEY_ARGS_END; +} + +mycell *allocheader(mps_ap_t ap, mps_word_t data, + mycell *ref0, mycell *ref1, size_t size, size_t header) { mps_addr_t p; mycell *q; @@ -63,12 +79,12 @@ mycell *allocone(mps_ap_t ap, mps_word_t data, } /* twiddle the value of size to make it aligned */ - size = (size+align-1) & ~(align-1); + size = (size+header+align-1) & ~(align-1); do { die(mps_reserve(&p, ap, size), "Reserve: "); - q=p; + q=(void *)((char *)p + header); q->tag = MCdata; q->data = data; q->size = size; @@ -79,6 +95,12 @@ mycell *allocone(mps_ap_t ap, mps_word_t data, return q; } +mycell *allocone(mps_ap_t ap, mps_word_t data, + mycell *ref0, mycell *ref1, size_t size) +{ + return allocheader(ap, data, ref0, ref1, size, 0); +} + mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) { MPS_SCAN_BEGIN(ss) @@ -86,9 +108,10 @@ mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) while (base < limit) { mycell *obj = base; + unsigned long data = (unsigned long)obj->data; mps_res_t res; - if (formatcomments) printf("Scan %p.\n", (void *)base); + commentif(formatcomments, "scan %lu at %p", data, obj); switch (obj->tag) { case MCpadsingle: @@ -102,8 +125,8 @@ 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: %p.\n", (void*)&(obj->ref[0])); - res = MPS_FIX(ss, (mps_addr_t *) &(obj->ref[0])); /* pun! */ + commentif(formatcomments, "fix %lu[0] -> %p", data, obj->ref[0]); + res = MPS_FIX12(ss, (mps_addr_t *) &(obj->ref[0])); /* pun! */ if (res != MPS_RES_OK) { return res; @@ -111,8 +134,8 @@ 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: %p.\n", (void*)&(obj->ref[1])); - res = MPS_FIX(ss, (mps_addr_t *) &(obj->ref[1])); /* pun! */ + commentif(formatcomments, "fix %lu[1] -> %p", data, obj->ref[1]); + res = MPS_FIX12(ss, (mps_addr_t *) &(obj->ref[1])); /* pun! */ if (res != MPS_RES_OK) { return res; @@ -154,7 +177,7 @@ void mycopy(mps_addr_t object, mps_addr_t to) /* mycell *toj = to; */ - if (formatcomments) printf("copy! %p -> %p\n", object, to); + commentif(formatcomments, "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! @@ -216,4 +239,3 @@ void myfwd(mps_addr_t object, mps_addr_t to) obj->tag = MCheart; obj->data = (mps_word_t) to; } - diff --git a/mps/test/test/testlib/myfmt.h b/mps/test/test/testlib/myfmt.h index fd3969ce95e..87db53e4e5e 100644 --- a/mps/test/test/testlib/myfmt.h +++ b/mps/test/test/testlib/myfmt.h @@ -42,5 +42,9 @@ extern struct mps_fmt_A_s fmtA; mycell *allocone(mps_ap_t ap, mps_word_t data, mycell *ref0, mycell *ref1, size_t size); -#endif +mycell *allocheader(mps_ap_t ap, mps_word_t data, + mycell *ref0, mycell *ref1, size_t size, size_t header); +void fmtargs(mps_arg_s args[MPS_ARGS_MAX]); + +#endif diff --git a/mps/test/test/testlib/newfmt.c b/mps/test/test/testlib/newfmt.c index c82e96e40b9..0ccc35a5557 100644 --- a/mps/test/test/testlib/newfmt.c +++ b/mps/test/test/testlib/newfmt.c @@ -210,7 +210,7 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) */ commentif(fixcomments, "fix %li[%i] -> %li", obj->data.id, i, obj->data.ref[i].id); - res = MPS_FIX(ss, (mps_addr_t *) &p); + res = MPS_FIX12(ss, (mps_addr_t *) &p); if (res != MPS_RES_OK) return res; obj->data.ref[i].addr = p; } diff --git a/mps/test/test/testlib/platform.c b/mps/test/test/testlib/platform.c index 23ffd36f234..27f8ea2d8b3 100644 --- a/mps/test/test/testlib/platform.c +++ b/mps/test/test/testlib/platform.c @@ -8,8 +8,7 @@ LONG mySEHFilter(LPEXCEPTION_POINTERS info) { LPEXCEPTION_RECORD er; - int write; - unsigned long address; + ULONG_PTR write, address; er = info->ExceptionRecord; @@ -23,6 +22,8 @@ LONG mySEHFilter(LPEXCEPTION_POINTERS info) { report("memoryop", "read"); } report("memoryaddr", "%ld", address); + report("abort", "true"); + report("assert_or_abort", "true"); myabort(); } diff --git a/mps/test/test/testlib/platform.h b/mps/test/test/testlib/platform.h index c10eb1b103d..86224174957 100644 --- a/mps/test/test/testlib/platform.h +++ b/mps/test/test/testlib/platform.h @@ -4,12 +4,8 @@ */ #ifdef MPS_OS_W3 -#ifdef MMQA_HEADER_mpsw3 -/* we may be required to include mpsw3.h on windows platforms */ -#include "mpsw3.h" -#endif +#include "mpswin.h" /* to trap access violations in the test harness */ LONG mySEHFilter(LPEXCEPTION_POINTERS); #endif - diff --git a/mps/test/test/testlib/rankfmt.c b/mps/test/test/testlib/rankfmt.c index 97c81da6c00..9226d1c6e94 100644 --- a/mps/test/test/testlib/rankfmt.c +++ b/mps/test/test/testlib/rankfmt.c @@ -362,7 +362,7 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) if (p != NULL) { commentif(fixcomments, "fix %li[assoc]", obj->data.id); q = p; - res = MPS_FIX(ss, (mps_addr_t *) &p); + res = MPS_FIX12(ss, (mps_addr_t *) &p); if (res != MPS_RES_OK) return res; if (p == NULL) { asserts(rank == mps_rank_weak(), @@ -387,7 +387,7 @@ static mps_res_t myscan(mps_ss_t ss, mps_addr_t base, mps_addr_t limit) commentif(fixcomments, "fix %li[%i] -> %li", obj->data.id, i, obj->data.ref[i].id); q = p; - res = MPS_FIX(ss, (mps_addr_t *) &p); + res = MPS_FIX12(ss, (mps_addr_t *) &p); if (p == NULL) { asserts(rank == mps_rank_weak(), "non-weak reference fixed to NULL at %p[i]", obj); diff --git a/mps/test/test/testlib/testlib.c b/mps/test/test/testlib/testlib.c index 4d43211a899..77fc3ab44a4 100644 --- a/mps/test/test/testlib/testlib.c +++ b/mps/test/test/testlib/testlib.c @@ -195,11 +195,9 @@ void myabort(void) { void verror(const char *format, va_list args) { - fprintf(stdout, "%% ERROR \n!error=true\n"); - fprintf(stdout, "!errtext="); - vfprintf(stdout, format, args); - fprintf(stdout, "\n"); - fflush(stdout); + comment("ERROR"); + report("error", "true"); + vreport("errtext", format, args); myabort(); } @@ -213,12 +211,11 @@ void asserts(int expr, const char *format, ...) va_list args; if (!expr) { + comment("TEST ASSERTION FAILURE"); + report("assert", "true"); + report("assert_or_abort", "true"); va_start(args, format); - fprintf(stdout, "%% ASSERTION FAILED \n!assert=true\n"); - fprintf(stdout, "!asserttext="); - vfprintf(stdout, format, args); - fprintf(stdout, "\n"); - fflush(stdout); + vreport("asserttext", format, args); va_end(args); myabort(); } @@ -234,14 +231,15 @@ void asserts(int expr, const char *format, ...) static void mmqa_assert_handler(const char *cond, const char *id, const char *file, unsigned line) { + comment("MPS ASSERTION FAILURE"); + report("assert", "true"); + report("assert_or_abort", "true"); if (line == 0) { /* assertion condition contains condition, file, line, separated by newline characters */ const char *val; - comment("MPS ASSERTION FAILURE"); - report("assert", "true"); report("assertid", "<no id supplied>"); fprintf(stdout, "!assertcond="); @@ -262,8 +260,6 @@ static void mmqa_assert_handler(const char *cond, const char *id, report("assertline", val); fflush(stdout); } else { - comment("MPS ASSERTION FAILURE"); - report("assert", "true"); report("assertid", id); report("assertfile", file); report("assertline", "%u", line); diff --git a/mps/test/testsets/argerr b/mps/test/testsets/argerr index 8c587bae4f7..383a3ddf3cc 100644 --- a/mps/test/testsets/argerr +++ b/mps/test/testsets/argerr @@ -22,31 +22,30 @@ argerr/15.c argerr/17.c argerr/18.c argerr/19.c -argerr/20.c +% argerr/20.c -- segfaults in hot variety (assertion is on the critical path) argerr/21.c -argerr/22.c +% argerr/22.c -- segfaults in hot variety (assertion is on the critical path) argerr/23.c -argerr/24.c +% argerr/24.c -- assertion in different place in the hot variety argerr/25.c argerr/26.c argerr/27.c -argerr/28.c -argerr/29.c -argerr/30.c -argerr/31.c +% argerr/28.c -- segfaults in hot variety (assertion is on the critical path) +% argerr/29.c -- fails in hot variety (assertion is on the critical path) +% argerr/30.c -- assertion in different place in the hot variety +% argerr/31.c -- assertion in different place in the hot variety argerr/32.c -argerr/33.c +% argerr/33.c -- assertion in different place in the hot variety argerr/34.c -argerr/35.c +% argerr/35.c -- assertion in different place in the hot variety argerr/36.c -argerr/37.c +% argerr/37.c -- assertion in different place in the hot variety argerr/38.c argerr/39.c argerr/40.c argerr/41.c argerr/42.c -argerr/43.c -argerr/44.c +% 43-44 -- no such test argerr/45.c argerr/46.c argerr/47.c diff --git a/mps/test/testsets/conerr b/mps/test/testsets/conerr index 1bf93cd0712..67e283cf7e5 100644 --- a/mps/test/testsets/conerr +++ b/mps/test/testsets/conerr @@ -1,4 +1,4 @@ -% This testset contains all the "argerr" test cases that pass in +% This testset contains all the "conerr" test cases that pass in % both the cool and hot varieties, together with comments explaining % why the other test cases fail. @@ -14,22 +14,22 @@ conerr/8.c conerr/9.c conerr/10.c conerr/11.c -% conerr/12.c -- job003889 +conerr/12.c conerr/13.c conerr/14.c conerr/15.c conerr/16.c % conerr/17.c -- fails in hot variety (assertion is on the critical path) conerr/18.c -conerr/19.c -conerr/20.c -conerr/21.c +% conerr/19.c -- assertion in different place in the hot variety +% conerr/20.c -- segfaults in hot variety (assertion is on the critical path) +% conerr/21.c -- segfaults in hot variety (assertion is on the critical path) % conerr/22.c -- segfaults in hot variety (assertion is on the critical path) % conerr/23.c -- segfaults in hot variety (assertion is on the critical path) conerr/24.c conerr/25.c -conerr/26.c -conerr/27.c +% conerr/26.c -- assertion in different place in the hot variety +% conerr/27.c -- segfaults in hot variety (assertion is on the critical path) conerr/28.c conerr/29.c conerr/30.c @@ -66,4 +66,13 @@ conerr/55.c conerr/56.c % conerr/57.c -- see <code/ld.c#.add.no-arena-check> % conerr/58.c -- see <code/ld.c#.stale.no-arena-check> -conerr/59.c +% conerr/59.c -- assertion is on critical path +conerr/60.c +conerr/61.c +conerr/62.c +conerr/63.c +conerr/64.c +conerr/65.c +conerr/66.c +conerr/67.c +conerr/68.c diff --git a/mps/test/testsets/coolonly b/mps/test/testsets/coolonly index 3125dcbedb5..038edb41a53 100644 --- a/mps/test/testsets/coolonly +++ b/mps/test/testsets/coolonly @@ -4,6 +4,14 @@ % Assertion in different place in the hot variety. argerr/16.c +argerr/24.c +argerr/30.c +argerr/31.c +argerr/33.c +argerr/35.c +argerr/37.c +conerr/19.c +conerr/26.c % Rank is not a structure type, so AVERT(Rank) does nothing. argerr/49.c @@ -28,7 +36,15 @@ argerr/123.c argerr/124.c % Assertion is on the critical path. +argerr/20.c +argerr/22.c +argerr/28.c +argerr/29.c conerr/17.c +conerr/20.c +conerr/21.c conerr/22.c conerr/23.c +conerr/27.c +conerr/59.c function/72.c diff --git a/mps/test/testsets/passing b/mps/test/testsets/passing index 85f880b07ce..9b6928d3ac2 100644 --- a/mps/test/testsets/passing +++ b/mps/test/testsets/passing @@ -10,7 +10,7 @@ function/1.c function/5.c function/6.c function/7.c -% function/8.c -- tries to exhaust memory by mps_arena_create +function/8.c function/9.c function/10.c function/11.c @@ -26,7 +26,7 @@ function/19.c function/20.c function/21.c function/22.c -% function/23.c -- interactive test, can't run unattended +% function/23.c -- infinite loop; also job003789 function/24.c function/25.c function/26.c @@ -71,7 +71,7 @@ function/66.c function/67.c % function/68.c -- infinite loop function/69.c -function/70.c +% function/70.c -- interactive test, no point in running unattended function/71.c % function/72.c -- fails in hot variety (assertion is on the critical path) function/73.c @@ -88,7 +88,7 @@ function/83.c % 84-95 -- no such test function/96.c function/97.c -% function/98.c -- tries to exhaust memory by mps_arena_create +function/98.c function/99.c function/100.c function/101.c @@ -121,8 +121,7 @@ function/128.c function/129.c % function/130.c -- job003789 % function/131.c -- job003789 -% function/132.c -- job003869 -function/133.c +% 132-133 -- no such test function/134.c function/135.c function/136.c @@ -135,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 +% 149 -- no such test function/150.c function/151.c function/152.c @@ -172,3 +171,6 @@ function/227.c function/228.c function/229.c function/231.c +function/232.c +function/233.c +function/234.c diff --git a/mps/tool/.renamed-gitignore b/mps/tool/.renamed-gitignore new file mode 120000 index 00000000000..c5c99a6a89c --- /dev/null +++ b/mps/tool/.renamed-gitignore @@ -0,0 +1 @@ +.p4ignore \ No newline at end of file diff --git a/mps/tool/branch b/mps/tool/branch index 5bb9b21c3ec..1283792e665 100755 --- a/mps/tool/branch +++ b/mps/tool/branch @@ -4,7 +4,7 @@ # Gareth Rees, Ravenbrook Limited, 2014-03-18 # # $Id$ -# Copyright (c) 2014 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2014-2016 Ravenbrook Limited. See end of file for license. # # # 1. INTRODUCTION @@ -40,9 +40,7 @@ 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) +CHILD_RE = r'(?:custom/({})/)?(?:{}|version/({}))$'.format(CUSTOMER_RE, TASK_BRANCH_RE, VERSION_RE) TASK_BRANCH_ENTRY = ''' <tr valign="top"> @@ -57,7 +55,8 @@ TASK_BRANCH_ENTRY = ''' VERSION_BRANCH_ENTRY = ''' <tr valign="top"> <td> <a href="{version}/">{version}</a> </td> - <td> None. </td> + <td> + </td> <td> <a href="https://info.ravenbrook.com/infosys/cgi/perfbrowse.cgi?@files+{depot}/project/{project}/{parent}/...@{changelevel}">{parent}/...@{changelevel}</a> </td> <td> {desc_html} @@ -151,7 +150,10 @@ def main(argv): if args.task: if not re.match(TASK_RE, args.task): raise Error(fmt("Invalid task: {task}")) - args.child = fmt('branch/{date}/{task}') + if args.parent == 'master': + args.child = fmt('branch/{date}/{task}') + else: + args.child = fmt('custom/{customer}/branch/{date}/{task}') print(fmt("child={child}")) elif args.version: # Deduce version number from code/version.c. @@ -170,9 +172,9 @@ def main(argv): 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): + if args.customer != m.group(1): raise Error(fmt("Customer mismatch between {parent} and {child}.")) - args.date, args.task, _, args.version = m.groups() + _, args.date, args.task, args.version = m.groups() if not args.description: args.description = fmt("Branching {parent} to {child}.") @@ -254,8 +256,8 @@ def main(argv): return ''.join([d['data'] for d in p4.run('print', filespec) if d['code'] == 'text']) - if not args.version: - # Task branch + if not args.version and not args.customer: + # Public task branch register('{depot}/project/{project}/branch/index.html', '(?=</table>\n)', TASK_BRANCH_ENTRY) args.git_name = fmt('{project}-{task}') @@ -276,7 +278,7 @@ def main(argv): # 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')) + args.uuid = uuid.uuid5(uuid.NAMESPACE_URL, str(args.child)) print(fmt("uuid={uuid}")) for repo in GF_REPOS: @@ -312,6 +314,8 @@ if __name__ == '__main__': # # 2016-02-13 RB Adapting to Git Fusion 2. # +# 2016-09-13 GDR Support for customer task branches. +# # # C. COPYRIGHT AND LICENCE # diff --git a/mps/tool/index.rst b/mps/tool/index.rst index 8edc44095af..178d88347dc 100644 --- a/mps/tool/index.rst +++ b/mps/tool/index.rst @@ -37,8 +37,8 @@ This document is not confidential. `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. + Linux, it is invoked from ``comm.gmk``, and on macOS, + where it is invoked from the Xcode project. ================= ========================================================== .. _branch: branch @@ -75,7 +75,7 @@ B. Document History C. Copyright and License ------------------------ -Copyright © 2002-2014 Ravenbrook Limited. All rights reserved. +Copyright © 2002-2018 Ravenbrook Limited. All rights reserved. <http://www.ravenbrook.com/> This is an open source license. Contact Ravenbrook for commercial licensing options. diff --git a/mps/tool/noaslr.c b/mps/tool/noaslr.c index 3ae73dc9362..7dbf58f8303 100644 --- a/mps/tool/noaslr.c +++ b/mps/tool/noaslr.c @@ -1,7 +1,7 @@ /* 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. + * Copyright (c) 2014-2016 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. @@ -16,6 +16,7 @@ * <https://llvm.org/svn/llvm-project/lldb/trunk/tools/darwin-debug/darwin-debug.cpp> */ +#include <errno.h> #include <spawn.h> #include <sys/wait.h> #include <stdio.h> @@ -31,6 +32,7 @@ int main(int argc, char **argv) pid_t pid; posix_spawnattr_t attr; int res, status = 1; + const char *program = argv[0]; char *default_argv[] = {"/bin/sh", NULL}; if (argc >= 2) @@ -39,16 +41,25 @@ int main(int argc, char **argv) argv = default_argv; res = posix_spawnattr_init(&attr); - if (res != 0) + if (res != 0) { + errno = res; + perror(program); return res; + } res = posix_spawnattr_setflags(&attr, _POSIX_SPAWN_DISABLE_ASLR); - if (res != 0) + if (res != 0) { + errno = res; + perror(program); return res; + } res = posix_spawn(&pid, argv[0], NULL, &attr, argv, environ); - if (res != 0) + if (res != 0) { + errno = res; + perror(program); return res; + } if (waitpid(pid, &status, 0) == -1) return 1; @@ -62,7 +73,7 @@ int main(int argc, char **argv) /* C. COPYRIGHT AND LICENSE * - * Copyright (C) 2014 Ravenbrook Limited <http://www.ravenbrook.com/>. + * Copyright (C) 2014-2016 Ravenbrook Limited <http://www.ravenbrook.com/>. * All rights reserved. This is an open source license. Contact * Ravenbrook for commercial licensing options. * diff --git a/mps/tool/release b/mps/tool/release index d47fc36e788..0e63cf294af 100755 --- a/mps/tool/release +++ b/mps/tool/release @@ -2,9 +2,9 @@ # # RELEASE -- MAKE A RELEASE # Gareth Rees, Ravenbrook Limited, 2014-03-18 -# +# # $Id$ -# Copyright (c) 2014 Ravenbrook Limited. See end of file for license. +# Copyright (c) 2014-2018 Ravenbrook Limited. See end of file for license. # # # 1. INTRODUCTION @@ -64,8 +64,8 @@ RELEASE_ENTRY = ''' </tr> ''' -VERSION_ENTRY = ''' <a href="/project/{project}/release/{release}/">{release}</a> -''' +VERSION_ENTRY = ''' + <a href="/project/{project}/release/{release}/">{release}</a>''' def main(argv): parser = argparse.ArgumentParser() @@ -202,12 +202,12 @@ def main(argv): register('{depot}/project/{project}/release/index.html', '(?<=<tbody>\n)', RELEASE_ENTRY) register('{depot}/project/{project}/version/index.html', - (r'(?<=<td><a href="{0}/">{0}</a></td>\n <td>\n)' - .format(re.escape(args.version), re.escape(args.project))), + (r'(?<=<td> <a href="{0}/">{0}</a> </td>\n <td>)' + .format(re.escape(args.version))), VERSION_ENTRY) register('{depot}/project/{project}/index.rst', r'release/\d+\.\d+\.\d+', 'release/{release}') - + if __name__ == '__main__': main(sys.argv) diff --git a/mps/tool/testcases.txt b/mps/tool/testcases.txt index b867b67acfc..1bce2ce7113 100644 --- a/mps/tool/testcases.txt +++ b/mps/tool/testcases.txt @@ -20,6 +20,7 @@ exposet0 =P expt825 finalcv =P finaltest =P +forktest =X fotest gcbench =N benchmark landtest @@ -37,6 +38,7 @@ poolncv qs sacss segsmss +sncss steptest =P tagtest teletest =N interactive diff --git a/mps/tool/testrun.bat b/mps/tool/testrun.bat index 5e9c0606ab7..4203af81e48 100755 --- a/mps/tool/testrun.bat +++ b/mps/tool/testrun.bat @@ -1,5 +1,5 @@ @rem $Id$ -@rem Copyright (c) 2013-2014 Ravenbrook Limited. See end of file for license. +@rem Copyright (c) 2013-2016 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 @@ -30,6 +30,7 @@ set LOGDIR=%TMP%\mps-%PFM%-%VARIETY%-log echo MPS test suite echo Logging test output to %LOGDIR% echo Test directory: %PFM%\%VARIETY% +echo Test case database: %TEST_CASE_DB% if exist %LOGDIR% rmdir /q /s %LOGDIR% mkdir %LOGDIR% @@ -49,7 +50,7 @@ set FAIL_COUNT=0 set SEPARATOR=---------------------------------------- if "%EXCLUDE%"=="" goto :args -for /f "tokens=1" %%T IN ('type %TEST_CASE_DB% ^|^ +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 @@ -87,7 +88,7 @@ exit /b @rem C. COPYRIGHT AND LICENSE @rem -@rem Copyright (C) 2013-2014 Ravenbrook Limited <http://www.ravenbrook.com/>. +@rem Copyright (C) 2013-2016 Ravenbrook Limited <http://www.ravenbrook.com/>. @rem All rights reserved. This is an open source license. Contact @rem Ravenbrook for commercial licensing options. @rem