diff --git a/mps/code/apss.c b/mps/code/apss.c index 79a029f86b1..7f579e54dda 100644 --- a/mps/code/apss.c +++ b/mps/code/apss.c @@ -169,13 +169,14 @@ static mps_pool_debug_option_s fenceOptions = { */ static void test(mps_arena_class_t arena_class, mps_arg_s arena_args[], + size_t arena_grain_size, mps_pool_debug_option_s *options) { mps_arena_t arena; die(mps_arena_create_k(&arena, arena_class, arena_args), "mps_arena_create"); MPS_ARGS_BEGIN(args) { - mps_align_t align = sizeof(void *) << (rnd() % 4); + mps_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); @@ -185,18 +186,19 @@ static void test(mps_arena_class_t arena_class, mps_arg_s arena_args[], mps_class_mvff(), args), "stress MVFF"); } MPS_ARGS_END(args); - /* IWBN to test MVFFDebug, but the MPS doesn't support debugging APs, */ - /* yet (MV Debug works here, because it fakes it through PoolAlloc). */ + /* IWBN to test MVFFDebug, but the MPS doesn't support debugging + APs, yet (MV Debug works here, because it fakes it through + PoolAlloc). See job003995. */ MPS_ARGS_BEGIN(args) { - mps_align_t align = (mps_align_t)1 << (rnd() % 6); + mps_align_t align = rnd_align(sizeof(void *), arena_grain_size); MPS_ARGS_ADD(args, MPS_KEY_ALIGN, align); die(stress(arena, NULL, align, randomSizeAligned, "MV", mps_class_mv(), args), "stress MV"); } MPS_ARGS_END(args); MPS_ARGS_BEGIN(args) { - mps_align_t align = (mps_align_t)1 << (rnd() % 6); + mps_align_t align = rnd_align(sizeof(void *), arena_grain_size); MPS_ARGS_ADD(args, MPS_KEY_ALIGN, align); MPS_ARGS_ADD(args, MPS_KEY_POOL_DEBUG_OPTIONS, options); die(stress(arena, options, align, randomSizeAligned, "MV debug", @@ -204,7 +206,7 @@ static void test(mps_arena_class_t arena_class, mps_arg_s arena_args[], } 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); die(stress(arena, NULL, align, randomSizeAligned, "MVT", mps_class_mvt(), args), "stress MVT"); @@ -218,28 +220,33 @@ static void test(mps_arena_class_t arena_class, mps_arg_s arena_args[], int main(int argc, char *argv[]) { + size_t arena_grain_size; + testlib_init(argc, argv); + arena_grain_size = rnd_grain(2 * testArenaSIZE); MPS_ARGS_BEGIN(args) { MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, 2 * testArenaSIZE); - MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, rnd_grain(2*testArenaSIZE)); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, arena_grain_size); MPS_ARGS_ADD(args, MPS_KEY_COMMIT_LIMIT, testArenaSIZE); - test(mps_arena_class_vm(), args, &fenceOptions); + test(mps_arena_class_vm(), args, arena_grain_size, &fenceOptions); } MPS_ARGS_END(args); + arena_grain_size = rnd_grain(2 * testArenaSIZE); MPS_ARGS_BEGIN(args) { MPS_ARGS_ADD(args, MPS_KEY_ARENA_SIZE, 2 * testArenaSIZE); MPS_ARGS_ADD(args, MPS_KEY_ARENA_ZONED, FALSE); - MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, rnd_grain(2*testArenaSIZE)); - test(mps_arena_class_vm(), args, &bothOptions); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, arena_grain_size); + test(mps_arena_class_vm(), args, arena_grain_size, &bothOptions); } MPS_ARGS_END(args); + 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_ZONED, FALSE); MPS_ARGS_ADD(args, MPS_KEY_ARENA_CL_BASE, malloc(testArenaSIZE)); - MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, rnd_grain(testArenaSIZE)); - test(mps_arena_class_cl(), args, &bothOptions); + MPS_ARGS_ADD(args, MPS_KEY_ARENA_GRAIN_SIZE, arena_grain_size); + test(mps_arena_class_cl(), args, arena_grain_size, &bothOptions); } MPS_ARGS_END(args); printf("%s: Conclusion: Failed to find any defects.\n", argv[0]); diff --git a/mps/code/commpre.nmk b/mps/code/commpre.nmk index d1aa7e49d6e..c428e909fa9 100644 --- a/mps/code/commpre.nmk +++ b/mps/code/commpre.nmk @@ -95,6 +95,7 @@ TEST_TARGETS=\ sacss.exe \ segsmss.exe \ steptest.exe \ + tagtest.exe \ teletest.exe \ walkt0.exe \ zcoll.exe \ diff --git a/mps/code/config.h b/mps/code/config.h index 734ab21a876..4bebdfb2d33 100644 --- a/mps/code/config.h +++ b/mps/code/config.h @@ -483,8 +483,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 */ @@ -668,6 +668,25 @@ } +/* 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 OS X 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 */ diff --git a/mps/code/gc.gmk b/mps/code/gc.gmk index 76716dc0785..f43e455dcd8 100644 --- a/mps/code/gc.gmk +++ b/mps/code/gc.gmk @@ -27,7 +27,7 @@ CFLAGSCOMPILER := \ -Wstrict-prototypes \ -Wswitch-default \ -Wwrite-strings -CFLAGSCOMPILERSTRICT := -ansi -pedantic +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/global.c b/mps/code/global.c index 2361b91caf4..2fdd038c88b 100644 --- a/mps/code/global.c +++ b/mps/code/global.c @@ -108,7 +108,6 @@ Bool GlobalsCheck(Globals arenaGlobals) TraceId ti; Trace trace; Index i; - Size depth; RefSet rs; Rank rank; @@ -155,20 +154,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)); @@ -294,13 +280,7 @@ Res GlobalsInit(Globals arenaGlobals) 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) { /* */ @@ -405,6 +385,7 @@ void GlobalsFinish(Globals arenaGlobals) arenaGlobals->sig = SigInvalid; + ShieldFinish(ArenaShield(arena)); RingFinish(&arena->formatRing); RingFinish(&arena->chainRing); RingFinish(&arena->messageRing); @@ -437,6 +418,7 @@ void GlobalsPrepareToDestroy(Globals arenaGlobals) ArenaPark(arenaGlobals); arena = GlobalsArena(arenaGlobals); + arenaDenounce(arena); defaultChain = arenaGlobals->defaultChain; @@ -488,6 +470,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 @@ -776,7 +760,7 @@ Bool ArenaStep(Globals globals, double interval, double multiplier) trace = ArenaTrace(arena, (TraceId)0); } else { /* No traces are running: consider collecting the world. */ - if (PolicyShouldCollectWorld(arena, availableEnd - now, now, + if (PolicyShouldCollectWorld(arena, (double)(availableEnd - now), now, clocks_per_sec)) { Res res; @@ -1000,7 +984,6 @@ 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, @@ -1019,13 +1002,7 @@ Res GlobalsDescribe(Globals arenaGlobals, mps_lib_FILE *stream, Count depth) 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 = ShieldDescribe(ArenaShield(arena), stream, depth); if (res != ResOK) return res; 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/mpm.c b/mps/code/mpm.c index aba1ac2f5cb..f49f05bb517 100644 --- a/mps/code/mpm.c +++ b/mps/code/mpm.c @@ -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,6 +653,148 @@ 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 diff --git a/mps/code/mpm.h b/mps/code/mpm.h index 36fccf1b1a6..07a3a38b9db 100644 --- a/mps/code/mpm.h +++ b/mps/code/mpm.h @@ -173,6 +173,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 * @@ -528,6 +537,7 @@ extern Ring GlobalsRememberedSummaryRing(Globals); #define ArenaGreyRing(arena, rank) (&(arena)->greyRing[rank]) #define ArenaPoolRing(arena) (&ArenaGlobals(arena)->poolRing) #define ArenaChunkTree(arena) RVALUE((arena)->chunkTree) +#define ArenaShield(arena) (&(arena)->shieldStruct) extern Bool ArenaGrainSizeCheck(Size size); #define AddrArenaGrainUp(addr, arena) AddrAlignUp(addr, ArenaGrainSize(arena)) @@ -901,14 +911,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) @@ -924,8 +939,8 @@ extern void (ShieldFlush)(Arena arena); 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." diff --git a/mps/code/mpmst.h b/mps/code/mpmst.h index a9fb95ba4cd..89de0297e31 100644 --- a/mps/code/mpmst.h +++ b/mps/code/mpmst.h @@ -253,13 +253,15 @@ typedef struct SegStruct { /* segment structure */ Tract firstTract; /* first tract of segment */ RingStruct poolRing; /* link in list of segs in pool */ Addr limit; /* limit of segment */ - 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; @@ -676,9 +678,46 @@ 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 */ + Bool inside; /* design.mps.shield.def.inside */ + 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 */ + Bool suspended; /* mutator suspended? */ + SortStruct sortStruct; /* workspace for queue sort */ +} ShieldStruct; + + /* ArenaStruct -- generic arena * - * See . */ + * See . + */ #define ArenaSig ((Sig)0x519A6E4A) /* SIGnature ARENA */ @@ -736,15 +775,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 */ diff --git a/mps/code/mpmtypes.h b/mps/code/mpmtypes.h index 2e0837940a2..f001038bc9f 100644 --- a/mps/code/mpmtypes.h +++ b/mps/code/mpmtypes.h @@ -112,6 +112,7 @@ typedef struct RangeStruct *Range; /* */ typedef struct LandStruct *Land; /* */ typedef struct LandClassStruct *LandClass; /* */ typedef unsigned FindDelete; /* */ +typedef struct ShieldStruct *Shield; /* design.mps.shield */ /* Arena*Method -- see */ diff --git a/mps/code/mps.xcodeproj/project.pbxproj b/mps/code/mps.xcodeproj/project.pbxproj index 192fb74ad56..f0d5b073df8 100644 --- a/mps/code/mps.xcodeproj/project.pbxproj +++ b/mps/code/mps.xcodeproj/project.pbxproj @@ -5324,7 +5324,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; @@ -5351,7 +5351,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; @@ -5746,7 +5763,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; @@ -5774,7 +5791,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; @@ -5788,7 +5822,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; @@ -5815,7 +5849,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; diff --git a/mps/code/poolmrg.c b/mps/code/poolmrg.c index 92b1819fa68..cc1cab67975 100644 --- a/mps/code/poolmrg.c +++ b/mps/code/poolmrg.c @@ -821,7 +821,7 @@ static Res MRGDescribe(Pool pool, mps_lib_FILE *stream, Count depth) 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); diff --git a/mps/code/poolmv.c b/mps/code/poolmv.c index 719cba6d7df..f3ef16f69ee 100644 --- a/mps/code/poolmv.c +++ b/mps/code/poolmv.c @@ -29,6 +29,7 @@ #include "dbgpool.h" #include "poolmv.h" #include "poolmfs.h" +#include "mpscmvff.h" #include "mpm.h" SRCID(poolmv, "$Id$"); @@ -236,7 +237,10 @@ static Res MVInit(Pool pool, ArgList args) if (ArgPick(&arg, args, MPS_KEY_MAX_SIZE)) maxSize = arg.val.size; + arena = PoolArena(pool); + AVERT(Align, align); + AVER(align <= ArenaGrainSize(arena)); AVER(extendBy > 0); AVER(avgSize > 0); AVER(avgSize <= extendBy); @@ -245,7 +249,6 @@ static Res MVInit(Pool pool, ArgList args) 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. */ diff --git a/mps/code/poolmv2.c b/mps/code/poolmv2.c index dc352d6eb81..fbfb8464e61 100644 --- a/mps/code/poolmv2.c +++ b/mps/code/poolmv2.c @@ -259,10 +259,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); diff --git a/mps/code/poolmvff.c b/mps/code/poolmvff.c index 82d5c072ff3..64acc2606ab 100644 --- a/mps/code/poolmvff.c +++ b/mps/code/poolmvff.c @@ -486,10 +486,10 @@ 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); 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/seg.c b/mps/code/seg.c index 04785652e90..22cfd98e7cb 100644 --- a/mps/code/seg.c +++ b/mps/code/seg.c @@ -1,7 +1,7 @@ /* seg.c: SEGMENTS * * $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. * * .design: The design for this module is . * @@ -16,14 +16,6 @@ * 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 */ #include "tract.h" @@ -157,7 +149,9 @@ static Res SegInit(Seg seg, Pool pool, Addr base, Size size, ArgList args) 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 */ @@ -213,6 +207,11 @@ static void SegFinish(Seg seg) AVERT(SegClass, class); 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); } @@ -223,7 +222,10 @@ static void SegFinish(Seg 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); @@ -572,7 +574,8 @@ Res SegMerge(Seg *mergedSegReturn, Seg segLo, Seg segHi) AVER(SegBase(segHi) == SegLimit(segLo)); 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); @@ -624,7 +627,9 @@ Res SegSplit(Seg *segLoReturn, Seg *segHiReturn, Seg seg, Addr at) * the split point. */ AVER(SegBuffer(seg) == NULL || BufferLimit(SegBuffer(seg)) <= 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); @@ -682,6 +687,8 @@ Bool SegCheck(Seg seg) CHECKL(AddrIsArenaGrain(TractBase(seg->firstTract), arena)); CHECKL(AddrIsArenaGrain(seg->limit, arena)); CHECKL(seg->limit > TractBase(seg->firstTract)); + /* Can't BoolCheck seg->queued because compilers warn about that on + single-bit fields. */ /* Each tract of the segment must agree about white traces. Note * that even if the CHECKs are compiled away there is still a @@ -710,8 +717,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 */ @@ -730,6 +746,7 @@ 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; } @@ -891,9 +908,11 @@ 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 */ @@ -930,7 +949,6 @@ static Res segNoSplit(Seg seg, Seg segHi, AVER(SegLimit(seg) == limit); NOTREACHED; return ResFAIL; - } @@ -956,9 +974,10 @@ static Res segTrivSplit(Seg seg, Seg segHi, AVER(SegBase(seg) == base); AVER(SegLimit(seg) == limit); - /* 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; @@ -970,6 +989,7 @@ 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; @@ -1311,6 +1331,16 @@ static void gcSegSetRankSet(Seg seg, RankSet rankSet) } +static void gcSegSyncWriteBarrier(Seg seg, Arena arena) +{ + /* 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 * * In fact, we only need to raise the write barrier if the @@ -1323,7 +1353,6 @@ static void gcSegSetRankSet(Seg seg, RankSet rankSet) static void gcSegSetSummary(Seg seg, RefSet summary) { GCSeg gcseg; - RefSet oldSummary; Arena arena; AVERT_CRITICAL(Seg, seg); /* .seg.method.check */ @@ -1332,19 +1361,11 @@ static void gcSegSetSummary(Seg seg, RefSet summary) 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); - } + gcSegSyncWriteBarrier(seg, arena); } @@ -1353,7 +1374,6 @@ static void gcSegSetSummary(Seg seg, RefSet summary) static void gcSegSetRankSummary(Seg seg, RankSet rankSet, RefSet summary) { GCSeg gcseg; - Bool wasShielded, willbeShielded; Arena arena; AVERT_CRITICAL(Seg, seg); /* .seg.method.check */ @@ -1369,17 +1389,11 @@ static void gcSegSetRankSummary(Seg seg, RankSet rankSet, RefSet summary) arena = PoolArena(SegPool(seg)); - wasShielded = (seg->rankSet != RankSetEMPTY && gcseg->summary != RefSetUNIV); - willbeShielded = (rankSet != RankSetEMPTY && summary != RefSetUNIV); - seg->rankSet = BS_BITFIELD(Rank, rankSet); gcseg->summary = summary; - if (willbeShielded && !wasShielded) { - ShieldRaise(arena, seg, AccessWRITE); - } else if (wasShielded && !willbeShielded) { - ShieldLower(arena, seg, AccessWRITE); - } + if (rankSet != RankSetEMPTY) + gcSegSyncWriteBarrier(seg, arena); } @@ -1674,7 +1688,7 @@ void SegClassMixInNoSplitMerge(SegClass class) /* 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/shield.c b/mps/code/shield.c index 0dfc1945db6..64ab135ff1e 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-2016 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,751 @@ SRCID(shield, "$Id$"); -void (ShieldSuspend)(Arena arena) +void ShieldInit(Shield shield) { - AVERT(Arena, arena); - AVER(arena->insideShield); + shield->inside = FALSE; + shield->queue = NULL; + shield->length = 0; + shield->next = 0; + shield->limit = 0; + shield->depth = 0; + shield->unsynced = 0; + shield->holds = 0; + shield->suspended = FALSE; + 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); + CHECKL(BoolCheck(shield->inside)); + CHECKL(shield->queue == NULL || shield->length > 0); + CHECKL(shield->limit <= shield->length); + CHECKL(shield->next <= shield->limit); + CHECKL(BoolCheck(shield->suspended)); + + /* 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); + + /* If any segment is exposed, the mutator is suspended. */ + CHECKL(shield->depth == 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->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 depth = 0; + Count unsynced = 0; + Index i; + for (i = 0; i < shield->limit; ++i) { + Seg seg = shield->queue[i]; + CHECKD(Seg, seg); + depth += SegDepth(seg); + if (!SegIsSynced(seg)) + ++unsynced; + } + CHECKL(depth == shield->depth); + CHECKL(unsynced == 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 OS X, 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_AVERT(Arena, arena); + SHIELD_AVERT(Seg, seg); AVERT(AccessSet, mode); - AVER((SegSM(seg) & mode) == AccessSetEMPTY); - SegSetSM(seg, SegSM(seg) | mode); /* inv.prot.shield preserved */ - /* 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); + + /* 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; 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 { + 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(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-2016 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..af96d3643fb 100644 --- a/mps/code/testlib.c +++ b/mps/code/testlib.c @@ -220,14 +220,30 @@ 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(1uL << log2min == min); + Insist(1uL << log2max == max); + if (log2min < log2max) + return min << (rnd() % (log2max - log2min + 1)); + else + return min; } rnd_state_t rnd_seed(void) diff --git a/mps/code/testlib.h b/mps/code/testlib.h index 0b5b7160165..0492aaf138b 100644 --- a/mps/code/testlib.h +++ b/mps/code/testlib.h @@ -260,6 +260,11 @@ 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); + + /* randomize -- randomize the generator, or initialize to replay * * randomize(argc, argv) randomizes the rnd generator (using time(3)) diff --git a/mps/code/trace.c b/mps/code/trace.c index 6d9cb0f0f98..10517d89640 100644 --- a/mps/code/trace.c +++ b/mps/code/trace.c @@ -408,6 +408,8 @@ Res TraceCondemnZones(Trace trace, ZoneSet condemnedSet) arena = trace->arena; + ShieldHold(arena); /* .whiten.hold */ + if(SegFirst(&seg, arena)) { do { /* Segment should be black now. */ @@ -430,6 +432,8 @@ Res TraceCondemnZones(Trace trace, ZoneSet condemnedSet) } while (SegNext(&seg, arena, seg)); } + ShieldRelease(arena); + EVENT3(TraceCondemnZones, trace, condemnedSet, trace->white); /* The trace's white set must be a subset of the condemned set */ @@ -438,6 +442,7 @@ Res TraceCondemnZones(Trace trace, ZoneSet condemnedSet) return ResOK; failBegin: + ShieldRelease(arena); AVER(TraceIsEmpty(trace)); /* See .whiten.fail. */ return res; } @@ -573,7 +578,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)); @@ -634,11 +639,11 @@ static Res traceFlip(Trace trace) EVENT2(TraceFlipEnd, trace, arena); - ShieldResume(arena); + ShieldRelease(arena); return ResOK; failRootFlip: - ShieldResume(arena); + ShieldRelease(arena); return res; } @@ -732,13 +737,6 @@ 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 ({ /* Iterate over all chains, all GenDescs within a chain, and all * PoolGens within a GenDesc. */ @@ -1118,6 +1116,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); @@ -1164,16 +1163,32 @@ static Res traceScanSegRes(TraceSet ts, Rank rank, Arena arena, Seg 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); } @@ -1214,24 +1229,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? */ + /* Write barrier deferral -- see design.mps.write-barrier.deferral. */ + if (writeHit) + seg->defer = WB_DEFER_HIT; + + if (readHit) { Trace trace; TraceId ti; Rank rank; @@ -1265,11 +1290,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); } @@ -1474,7 +1499,7 @@ 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); } @@ -1490,6 +1515,14 @@ static Res traceCondemnAll(Trace trace) arena = trace->arena; AVERT(Arena, arena); + /* .whiten.hold: 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. */ + ShieldHold(arena); + /* Condemn all segments in pools with the GC attribute. */ RING_FOR(poolNode, &ArenaGlobals(arena)->poolRing, nextPoolNode) { Pool pool = RING_ELT(Pool, arenaRing, poolNode); @@ -1508,6 +1541,8 @@ static Res traceCondemnAll(Trace trace) } } + ShieldRelease(arena); + if (TraceIsEmpty(trace)) return ResFAIL; @@ -1528,6 +1563,7 @@ static Res traceCondemnAll(Trace trace) * will be triggered. In that case, we'll have to recover here by * blackening the segments again. */ AVER(TraceIsEmpty(trace)); + ShieldRelease(arena); return res; } diff --git a/mps/code/xci3ll.gmk b/mps/code/xci3ll.gmk new file mode 100644 index 00000000000..a0af3b7079d --- /dev/null +++ b/mps/code/xci3ll.gmk @@ -0,0 +1,72 @@ +# -*- makefile -*- +# +# xci3ll.gmk: BUILD FOR MAC OS X/i386/Clang PLATFORM +# +# $Id$ +# Copyright (c) 2001-2014 Ravenbrook Limited. See end of file for license. +# +# .prefer.xcode: The documented and preferred way to develop the MPS +# for this platform is to use the Xcode project (mps.xcodeproj). This +# makefile provides a way to compile the MPS one source file at a +# time, rather than all at once via mps.c (which can hide errors due +# to missing headers). + +PFM = xci3ll + +MPMPF = \ + lockix.c \ + prmci3xc.c \ + proti3.c \ + protix.c \ + protxc.c \ + span.c \ + ssixi3.c \ + thxc.c \ + vmix.c + +include ll.gmk + +CC = clang -arch i386 + +include comm.gmk + + +# C. COPYRIGHT AND LICENSE +# +# Copyright (C) 2001-2014 Ravenbrook Limited . +# All rights reserved. This is an open source license. Contact +# Ravenbrook for commercial licensing options. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# 3. Redistributions in any form must be accompanied by information on how +# to obtain complete source code for this software and any accompanying +# software that uses this software. The source code must either be +# included in the distribution or be available for no more than the cost +# of distribution plus a nominal fee, and must be freely redistributable +# under reasonable conditions. For an executable file, complete source +# code means the source code for all modules it contains. It does not +# include source code for modules or files that typically accompany the +# major components of the operating system on which the executable file +# runs. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +# PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/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 c7928ec14bb..436aad238c6 100644 --- a/mps/configure.ac +++ b/mps/configure.ac @@ -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 diff --git a/mps/design/abq.txt b/mps/design/abq.txt index cc0cfe0159f..75aaec3d1a2 100644 --- a/mps/design/abq.txt +++ b/mps/design/abq.txt @@ -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..4dac0fe9fb1 100644 --- a/mps/design/alloc-frame.txt +++ b/mps/design/alloc-frame.txt @@ -473,8 +473,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 70f744e9095..12a25e7aec2 100644 --- a/mps/design/an.txt +++ b/mps/design/an.txt @@ -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 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 1d7f412dd86..7d6fe4c0eb7 100644 --- a/mps/design/arena.txt +++ b/mps/design/arena.txt @@ -616,8 +616,8 @@ Document History Copyright and License --------------------- -Copyright © 2001-2014 Ravenbrook Limited. All rights reserved. -. This is an open source license. Contact +Copyright © 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 diff --git a/mps/design/arenavm.txt b/mps/design/arenavm.txt index d746f18d883..c43a35bb3d0 100644 --- a/mps/design/arenavm.txt +++ b/mps/design/arenavm.txt @@ -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-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/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 04b8779e2ca..a15a63c76f0 100644 --- a/mps/design/buffer.txt +++ b/mps/design/buffer.txt @@ -736,8 +736,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/check.txt b/mps/design/check.txt index 138c2a7fc1d..2cf5bf091c6 100644 --- a/mps/design/check.txt +++ b/mps/design/check.txt @@ -138,8 +138,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/class-interface.txt b/mps/design/class-interface.txt index 65cc8accb2a..00925e51019 100644 --- a/mps/design/class-interface.txt +++ b/mps/design/class-interface.txt @@ -264,8 +264,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/clock.txt b/mps/design/clock.txt index d9b73d43081..3213d6ccff5 100644 --- a/mps/design/clock.txt +++ b/mps/design/clock.txt @@ -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..544b9a61ed1 100644 --- a/mps/design/config.txt +++ b/mps/design/config.txt @@ -610,8 +610,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/diag.txt b/mps/design/diag.txt index f8e86e2bdda..15384db203b 100644 --- a/mps/design/diag.txt +++ b/mps/design/diag.txt @@ -193,8 +193,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/exec-env.txt b/mps/design/exec-env.txt index 29576212b4a..71d7287f057 100644 --- a/mps/design/exec-env.txt +++ b/mps/design/exec-env.txt @@ -149,8 +149,8 @@ Document History Copyright and License --------------------- -Copyright © 1996-2016 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/finalize.txt b/mps/design/finalize.txt index e3668716df7..b8e47021a0a 100644 --- a/mps/design/finalize.txt +++ b/mps/design/finalize.txt @@ -139,8 +139,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/fix.txt b/mps/design/fix.txt index dce6b561c84..7bd8b3a9b89 100644 --- a/mps/design/fix.txt +++ b/mps/design/fix.txt @@ -73,8 +73,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/freelist.txt b/mps/design/freelist.txt index c89181505c1..c86ae9a9f14 100644 --- a/mps/design/freelist.txt +++ b/mps/design/freelist.txt @@ -172,8 +172,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.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.review.txt b/mps/design/guide.review.txt index 4bed3049842..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-2016 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 26df05b3126..57fced19a93 100644 --- a/mps/design/index.txt +++ b/mps/design/index.txt @@ -114,6 +114,7 @@ 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 ====================== ================================================ @@ -192,6 +193,7 @@ writef_ The WriteF function .. _vm: vm .. _vmo1: vmo1 .. _vmso: vmso +.. _write-barrier: write-barrier .. _writef: writef @@ -225,6 +227,7 @@ Document History - 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. .. _RB: http://www.ravenbrook.com/consultants/rb .. _NB: http://www.ravenbrook.com/consultants/nb @@ -235,8 +238,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/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..ed1bbcd14a7 100644 --- a/mps/design/lock.txt +++ b/mps/design/lock.txt @@ -287,8 +287,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/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..1c5d4210914 100644 --- a/mps/design/message.txt +++ b/mps/design/message.txt @@ -407,8 +407,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/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 627f8352c3a..a803b16eb1d 100644 --- a/mps/design/object-debug.txt +++ b/mps/design/object-debug.txt @@ -427,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..e54f55b60b0 100644 --- a/mps/design/pool.txt +++ b/mps/design/pool.txt @@ -78,8 +78,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/poolamc.txt b/mps/design/poolamc.txt index f00bc4adbae..cd1612e982d 100644 --- a/mps/design/poolamc.txt +++ b/mps/design/poolamc.txt @@ -813,8 +813,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/poolams.txt b/mps/design/poolams.txt index ef59e46724a..30a4efc10e7 100644 --- a/mps/design/poolams.txt +++ b/mps/design/poolams.txt @@ -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-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/poolawl.txt b/mps/design/poolawl.txt index 609d236320e..88d038b80b0 100644 --- a/mps/design/poolawl.txt +++ b/mps/design/poolawl.txt @@ -565,8 +565,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/poollo.txt b/mps/design/poollo.txt index ebb402c316b..aae70799452 100644 --- a/mps/design/poollo.txt +++ b/mps/design/poollo.txt @@ -265,8 +265,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..4ed24263308 100644 --- a/mps/design/poolmfs.txt +++ b/mps/design/poolmfs.txt @@ -44,8 +44,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/poolmrg.txt b/mps/design/poolmrg.txt index 148e016fae7..9c8de7e092e 100644 --- a/mps/design/poolmrg.txt +++ b/mps/design/poolmrg.txt @@ -685,8 +685,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/poolmv.txt b/mps/design/poolmv.txt index 1b25c441a27..ca51cc59c6c 100644 --- a/mps/design/poolmv.txt +++ b/mps/design/poolmv.txt @@ -47,8 +47,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/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/prmc.txt b/mps/design/prmc.txt index c7aaad52d57..e633729798f 100644 --- a/mps/design/prmc.txt +++ b/mps/design/prmc.txt @@ -271,8 +271,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/prot.txt b/mps/design/prot.txt index fb06b78b328..0cdd8d38fcd 100644 --- a/mps/design/prot.txt +++ b/mps/design/prot.txt @@ -165,8 +165,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/protli.txt b/mps/design/protli.txt index 8d4a2f7b395..d07ee96065e 100644 --- a/mps/design/protli.txt +++ b/mps/design/protli.txt @@ -217,8 +217,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/protocol.txt b/mps/design/protocol.txt index 4dca17bcf97..38f29062913 100644 --- a/mps/design/protocol.txt +++ b/mps/design/protocol.txt @@ -540,8 +540,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/protsu.txt b/mps/design/protsu.txt index e31edf3fe73..22d9dca5224 100644 --- a/mps/design/protsu.txt +++ b/mps/design/protsu.txt @@ -131,8 +131,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/pthreadext.txt b/mps/design/pthreadext.txt index 18b7b2d9d9f..b2d886b75a5 100644 --- a/mps/design/pthreadext.txt +++ b/mps/design/pthreadext.txt @@ -368,8 +368,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/range.txt b/mps/design/range.txt index 9e6d4ce34a4..ee1e734ea40 100644 --- a/mps/design/range.txt +++ b/mps/design/range.txt @@ -124,8 +124,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/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..ef302553384 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,7 +32,7 @@ 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 diff --git a/mps/design/seg.txt b/mps/design/seg.txt index 1a25926ffd5..dc9e8b96083 100644 --- a/mps/design/seg.txt +++ b/mps/design/seg.txt @@ -268,14 +268,14 @@ design.mps.protocol.overview.next-method_). .. _design.mps.protocol.overview.next-method: protocol#overview.next-method _`.split-merge.shield`: Split and merge methods may assume that the -segments they are manipulating are not in the shield cache. +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 diff --git a/mps/design/shield.txt b/mps/design/shield.txt index 31648245ed1..9b56c96a466 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,8 +25,10 @@ _`.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. @@ -38,117 +40,227 @@ 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). +``ShieldEnter`` and ``ShieldLeave`` are called by ``ArenaEnter`` 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 +............................ -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. +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. -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. +A segment might for example be exposed during: -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. + - 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). -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. +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. -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. +Similarly, a pool class that never raises the shield on its segments +need never expose them to gain access. -.. note:: - 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. +Collector access to the unprotectable +..................................... - 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? +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: - 3. Why is ``seg->depth`` never checked for overflow? It is only a - 4-bit-wide bit field, currently. + - 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. - Richard Kistruck, 2006-12-19. +``void ShieldHold(Arena arena)`` + + Get exclusive access to the unprotectable. + +``void ShieldRelease(Arena arena)`` + + 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 OS X. + +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 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, where + | #exposes = the number of calls to ``ShieldExpose`` on the seg + | #covers = the number of calls to ``ShieldCover`` on the seg + +``ShieldCover`` should not be called without a matching +``ShieldExpose``, 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 +``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 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 seg's depth is greater than zero. + +_`.inv.expose.prot`: An exposed seg 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 segs are synced (by .inv.unsynced.depth) + +_`.proof.access`: If the mutator is running then all segs 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 +270,99 @@ _`.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 +.................... + +_`.improve.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 .improve.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 for GC). 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 OS X, the main cost seems to be the + TLB flush, which affects wall-clock time of everything on the + processor! + + References ---------- @@ -183,14 +388,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-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/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..30c8fa62f35 100644 --- a/mps/design/sp.txt +++ b/mps/design/sp.txt @@ -186,8 +186,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/splay.txt b/mps/design/splay.txt index 3a2ed9fd546..c1dfa8bb977 100644 --- a/mps/design/splay.txt +++ b/mps/design/splay.txt @@ -955,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/stack-scan.txt b/mps/design/stack-scan.txt index 601d9c780ee..ea29580740c 100644 --- a/mps/design/stack-scan.txt +++ b/mps/design/stack-scan.txt @@ -327,8 +327,8 @@ Document History Copyright and License --------------------- -Copyright © 2014-2016 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/strategy.txt b/mps/design/strategy.txt index 51b32e9e31a..97aa86a4a08 100644 --- a/mps/design/strategy.txt +++ b/mps/design/strategy.txt @@ -557,8 +557,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..31cfffb0bf2 100644 --- a/mps/design/tests.txt +++ b/mps/design/tests.txt @@ -76,8 +76,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/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..1d6d585a9d6 100644 --- a/mps/design/thread-manager.txt +++ b/mps/design/thread-manager.txt @@ -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-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-safety.txt b/mps/design/thread-safety.txt index 826d008a596..9e3df1de20b 100644 --- a/mps/design/thread-safety.txt +++ b/mps/design/thread-safety.txt @@ -349,8 +349,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/trace.txt b/mps/design/trace.txt index 236a0cae5d9..a2869145155 100644 --- a/mps/design/trace.txt +++ b/mps/design/trace.txt @@ -295,8 +295,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 008a54a209c..365de98b1a7 100644 --- a/mps/design/type.txt +++ b/mps/design/type.txt @@ -717,8 +717,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/version-library.txt b/mps/design/version-library.txt index b679c382713..0afb604e690 100644 --- a/mps/design/version-library.txt +++ b/mps/design/version-library.txt @@ -133,8 +133,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/version.txt b/mps/design/version.txt index 798c01ead99..15e661c9513 100644 --- a/mps/design/version.txt +++ b/mps/design/version.txt @@ -61,8 +61,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/vm.txt b/mps/design/vm.txt index fb1be6eb5da..72babab8396 100644 --- a/mps/design/vm.txt +++ b/mps/design/vm.txt @@ -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-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/vmo1.txt b/mps/design/vmo1.txt index 33c90858439..2bb33a4f182 100644 --- a/mps/design/vmo1.txt +++ b/mps/design/vmo1.txt @@ -72,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-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/vmso.txt b/mps/design/vmso.txt index a73a0d65cbd..9a06c26950b 100644 --- a/mps/design/vmso.txt +++ b/mps/design/vmso.txt @@ -159,8 +159,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/write-barrier.txt b/mps/design/write-barrier.txt new file mode 100644 index 00000000000..3b54c882964 --- /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 OS X 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 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/manual/source/design/index.rst b/mps/manual/source/design/index.rst index 0f4a989896a..b71d2c731c7 100644 --- a/mps/manual/source/design/index.rst +++ b/mps/manual/source/design/index.rst @@ -29,12 +29,14 @@ Design prot range ring - sp + shield sig + sp splay stack-scan testthr thread-manager type vm + write-barrier writef diff --git a/mps/manual/source/design/old.rst b/mps/manual/source/design/old.rst index e91df0b1df5..3bbc777dabd 100644 --- a/mps/manual/source/design/old.rst +++ b/mps/manual/source/design/old.rst @@ -48,7 +48,6 @@ Old design root scan seg - shield strategy telemetry tests diff --git a/mps/manual/source/pool/mv.rst b/mps/manual/source/pool/mv.rst index 27e691ed3af..b8cfb08d2ea 100644 --- a/mps/manual/source/pool/mv.rst +++ b/mps/manual/source/pool/mv.rst @@ -70,10 +70,11 @@ MV 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. + :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 1 (one) + and the maximum is the arena grain size + (see :c:macro:`MPS_KEY_ARENA_GRAIN_SIZE`). * :c:macro:`MPS_KEY_EXTEND_BY` (type :c:type:`size_t`, default 65536) is the :term:`size` of block that the pool will diff --git a/mps/manual/source/pool/mvff.rst b/mps/manual/source/pool/mvff.rst index 04a7d48603d..fd890f17dc9 100644 --- a/mps/manual/source/pool/mvff.rst +++ b/mps/manual/source/pool/mvff.rst @@ -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..8973823f948 100644 --- a/mps/manual/source/pool/mvt.rst +++ b/mps/manual/source/pool/mvt.rst @@ -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/release.rst b/mps/manual/source/release.rst index 1a70fd0b0b2..857daf78583 100644 --- a/mps/manual/source/release.rst +++ b/mps/manual/source/release.rst @@ -141,6 +141,20 @@ Other changes .. _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 is + poorly implemented. See job003371_ and job003975_. + +#. The MPS coalesces memory protection, reducing the number of system + calls. This drastically improves real run time on operating systems + where memory protection is poorly implemented, 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: diff --git a/mps/manual/source/topic/arena.rst b/mps/manual/source/topic/arena.rst index 2d6b99ca0db..d538402cba9 100644 --- a/mps/manual/source/topic/arena.rst +++ b/mps/manual/source/topic/arena.rst @@ -150,9 +150,9 @@ 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 diff --git a/mps/manual/source/topic/format.rst b/mps/manual/source/topic/format.rst index 7db5c8a1178..ec1f707aea3 100644 --- a/mps/manual/source/topic/format.rst +++ b/mps/manual/source/topic/format.rst @@ -368,6 +368,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/readme.txt b/mps/readme.txt index 2959f6eb7b4..08f0fe40b8a 100644 --- a/mps/readme.txt +++ b/mps/readme.txt @@ -77,7 +77,7 @@ The MPS is currently supported for deployment on: - Linux 2.4 or later, on IA-32 using GCC and on x86-64 using GCC or Clang/LLVM; -- FreeBSD 7 or later, on IA-32 and x86-64, using GCC; +- 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. @@ -132,6 +132,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 +144,7 @@ Document History Copyright and Licence --------------------- -Copyright (C) 2001-2014 Ravenbrook Limited. All rights reserved. +Copyright (C) 2001-2016 Ravenbrook Limited. All rights reserved. . This is an open source license. Contact Ravenbrook for commercial licensing options.