summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRick Hudson <rlh@golang.org>2014-11-04 13:31:34 -0500
committerRick Hudson <rlh@golang.org>2014-11-04 13:31:34 -0500
commit9155768e48e7de0fd0d6297eaa932d4c489e3f38 (patch)
treec3fa5804642a64da1e36b1f9a915a8f463a89559
parentf65bb028c5ceb4fb213b103f24a85f17cf67ac39 (diff)
downloadgo-9155768e48e7de0fd0d6297eaa932d4c489e3f38.tar.gz
[dev.garbage] runtime: Add gc mark verification pass.
This adds an independent mark phase to the GC that can be used to verify the the default concurrent mark phase has found all reachable objects. It uses the upper 2 bits of the boundary nibble to encode the mark leaving the lower bits to encode the boundary and the normal mark bit. LGTM=rsc R=rsc CC=golang-codereviews https://codereview.appspot.com/167130043
-rw-r--r--src/runtime/heapdump.c15
-rw-r--r--src/runtime/malloc.go28
-rw-r--r--src/runtime/mgc0.c428
-rw-r--r--src/runtime/mgc0.h8
-rw-r--r--src/runtime/stack.c40
-rw-r--r--src/runtime/stubs.go6
6 files changed, 382 insertions, 143 deletions
diff --git a/src/runtime/heapdump.c b/src/runtime/heapdump.c
index 71da419f1..5ac37803b 100644
--- a/src/runtime/heapdump.c
+++ b/src/runtime/heapdump.c
@@ -259,20 +259,7 @@ dumpbv(BitVector *bv, uintptr offset)
dumpint(offset + i / BitsPerPointer * PtrSize);
break;
case BitsMultiWord:
- switch(bv->bytedata[(i+BitsPerPointer)/8] >> (i+BitsPerPointer)%8 & 3) {
- default:
- runtime·throw("unexpected garbage collection bits");
- case BitsIface:
- dumpint(FieldKindIface);
- dumpint(offset + i / BitsPerPointer * PtrSize);
- i += BitsPerPointer;
- break;
- case BitsEface:
- dumpint(FieldKindEface);
- dumpint(offset + i / BitsPerPointer * PtrSize);
- i += BitsPerPointer;
- break;
- }
+ runtime·throw("bumpbv unexpected garbage collection bits");
}
}
}
diff --git a/src/runtime/malloc.go b/src/runtime/malloc.go
index 56f4f7cd7..274bae9a3 100644
--- a/src/runtime/malloc.go
+++ b/src/runtime/malloc.go
@@ -306,6 +306,18 @@ func mallocgc(size uintptr, typ *_type, flags int) unsafe.Pointer {
}
}
marked:
+
+ // GCmarkterminate allocates black
+ // All slots hold nil so no scanning is needed.
+ // This may be racing with GC so do it atomically if there can be
+ // a race marking the bit.
+ if gcphase == _GCmarktermination {
+ mp := acquirem()
+ mp.ptrarg[0] = x
+ onM(gcmarknewobject_m)
+ releasem(mp)
+ }
+
if raceenabled {
racemalloc(x, size)
}
@@ -478,8 +490,12 @@ func gogc(force int32) {
// Do a concurrent heap scan before we stop the world.
onM(gcscan_m)
+ onM(gcinstallmarkwb_m)
onM(stoptheworld)
-
+ // onM(starttheworld)
+ // mark from roots scanned in gcscan_m. startthework when write barrier works
+ onM(gcmark_m)
+ // onM(stoptheworld)
if mp != acquirem() {
gothrow("gogc: rescheduled")
}
@@ -510,6 +526,8 @@ func gogc(force int32) {
onM(gc_m)
}
+ onM(gccheckmark_m)
+
// all done
mp.gcing = 0
semrelease(&worldsema)
@@ -524,6 +542,14 @@ func gogc(force int32) {
}
}
+func GCcheckmarkenable() {
+ onM(gccheckmarkenable_m)
+}
+
+func GCcheckmarkdisable() {
+ onM(gccheckmarkdisable_m)
+}
+
// GC runs a garbage collection.
func GC() {
gogc(2)
diff --git a/src/runtime/mgc0.c b/src/runtime/mgc0.c
index f76d7c05c..e283f6ee8 100644
--- a/src/runtime/mgc0.c
+++ b/src/runtime/mgc0.c
@@ -155,12 +155,16 @@ extern int32 runtime·gcpercent;
//
uint32 runtime·worldsema = 1;
+// It is a bug if bits does not have bitBoundary set but
+// there are still some cases where this happens related
+// to stack spans.
typedef struct Markbits Markbits;
struct Markbits {
byte *bitp; // pointer to the byte holding xbits
byte shift; // bits xbits needs to be shifted to get bits
byte xbits; // byte holding all the bits from *bitp
- byte bits; // bits relevant to corresponding slot.
+ byte bits; // mark and boundary bits relevant to corresponding slot.
+ byte tbits; // pointer||scalar bits relevant to corresponding slot.
};
extern byte runtime·data[];
@@ -204,6 +208,11 @@ static bool inheap(byte*);
static bool shaded(byte*);
static void shade(byte*);
static void slottombits(byte*, Markbits*);
+static void atomicxor8(byte*, byte);
+static bool ischeckmarked(Markbits*);
+static bool ismarked(Markbits*);
+static void clearcheckmarkbits(void);
+static void clearcheckmarkbitsspan(MSpan*);
void runtime·bgsweep(void);
void runtime·finishsweep_m(void);
@@ -228,6 +237,28 @@ struct WorkData {
};
WorkData runtime·work;
+// To help debug the concurrent GC we remark with the world
+// stopped ensuring that any object encountered has their normal
+// mark bit set. To do this we use an orthogonal bit
+// pattern to indicate the object is marked. The following pattern
+// uses the upper two bits in the object's bounday nibble.
+// 01: scalar not marked
+// 10: pointer not marked
+// 11: pointer marked
+// 00: scalar marked
+// Xoring with 01 will flip the pattern from marked to unmarked and vica versa.
+// The higher bit is 1 for pointers and 0 for scalars, whether the object
+// is marked or not.
+// The first nibble no longer holds the bitsDead pattern indicating that the
+// there are no more pointers in the object. This information is held
+// in the second nibble.
+
+// When marking an object if the bool checkmark is true one uses the above
+// encoding, otherwise one uses the bitMarked bit in the lower two bits
+// of the nibble.
+static bool checkmark = false;
+static bool gccheckmarkenable = false;
+
// Is address b in the known heap. If it doesn't have a valid gcmap
// returns false. For example pointers into stacks will return false.
static bool
@@ -261,11 +292,14 @@ slottombits(byte *obj, Markbits *mbits)
mbits->shift = (off % wordsPerBitmapByte) * gcBits;
mbits->xbits = *mbits->bitp;
mbits->bits = (mbits->xbits >> mbits->shift) & bitMask;
+ mbits->tbits = (mbits->xbits >> mbits->shift) & bitPtrMask;
}
// b is a pointer into the heap.
// Find the start of the object refered to by b.
// Set mbits to the associated bits from the bit map.
+// If b is not a valid heap object return nil and
+// undefined values in mbits.
static byte*
objectstart(byte *b, Markbits *mbits)
{
@@ -277,42 +311,27 @@ objectstart(byte *b, Markbits *mbits)
obj = (byte*)((uintptr)b&~(PtrSize-1));
for(;;) {
slottombits(obj, mbits);
- if(mbits->bits&bitBoundary == bitBoundary)
+ if((mbits->bits&bitBoundary) == bitBoundary)
break;
-
+
// Not a beginning of a block, consult span table to find the block beginning.
k = (uintptr)obj>>PageShift;
x = k;
x -= (uintptr)runtime·mheap.arena_start>>PageShift;
s = runtime·mheap.spans[x];
if(s == nil || k < s->start || obj >= s->limit || s->state != MSpanInUse){
- if(s->state == MSpanStack)
- break; // This is legit.
-
- // The following is catching some bugs left over from
- // us not being rigerous about what data structures are
- // hold valid pointers and different parts of the system
- // considering different structures as roots. For example
- // if there is a pointer into a stack that is left in
- // a global data structure but that part of the runtime knows that
- // those structures will be reinitialized before they are
- // reused. Unfortunately the GC believes these roots are valid.
- // Typically a stack gets moved and only the structures that part of
- // the system knows are alive are updated. The span is freed
- // after the stack copy and the pointer is still alive. This
- // check is catching that bug but for now we will not throw,
- // instead we will simply break out of this routine and depend
- // on the caller to recognize that this pointer is not a valid
- // heap pointer. I leave the code that catches the bug so that once
- // resolved we can turn this check back on and throw.
-
- //runtime·printf("Runtime: Span weird: obj=%p, k=%p", obj, k);
- //if (s == nil)
- // runtime·printf(" s=nil\n");
- //else
- // runtime·printf(" s->start=%p s->limit=%p, s->state=%d\n", s->start*PageSize, s->limit, s->state);
- //runtime·throw("Blowup on weird span");
- break; // We are not in a real block throw??
+ if(s != nil && s->state == MSpanStack) {
+ return nil; // This is legit.
+ }
+
+ // The following ensures that we are rigorous about what data
+ // structures hold valid pointers
+ runtime·printf("runtime:objectstart Span weird: obj=%p, k=%p", obj, k);
+ if (s == nil)
+ runtime·printf(" s=nil\n");
+ else
+ runtime·printf(" s->start=%p s->limit=%p, s->state=%d\n", s->start*PageSize, s->limit, s->state);
+ runtime·throw("objectstart: bad span");
}
p = (byte*)((uintptr)s->start<<PageShift);
if(s->sizeclass != 0) {
@@ -333,6 +352,75 @@ objectstart(byte *b, Markbits *mbits)
return obj;
}
+// Slow for now as we serialize this, since this is on a debug path
+// speed is not critical at this point.
+static Mutex xorlock;
+static void
+atomicxor8(byte *src, byte val)
+{
+ runtime·lock(&xorlock);
+ *src = *src^val;
+ runtime·unlock(&xorlock);
+}
+
+// Mark using the checkmark scheme.
+void
+docheckmark(Markbits *mbits)
+{
+ // xor 01 moves 01(scalar unmarked) to 00(scalar marked)
+ // and 10(pointer unmarked) to 11(pointer marked)
+ atomicxor8(mbits->bitp, BitsCheckMarkXor<<mbits->shift<<2);
+ return;
+}
+
+// In the default scheme does mbits refer to a marked object.
+static bool
+ismarked(Markbits *mbits)
+{
+ if((mbits->bits&bitBoundary) != bitBoundary)
+ runtime·throw("ismarked: bits should have boundary bit set");
+ return (mbits->bits&bitMarked) == bitMarked;
+}
+
+// In the checkmark scheme does mbits refer to a marked object.
+static bool
+ischeckmarked(Markbits *mbits)
+{
+ if((mbits->bits&bitBoundary) != bitBoundary)
+ runtime·printf("runtime:ischeckmarked: bits should have boundary bit set\n");
+ return mbits->tbits==BitsScalarMarked || mbits->tbits==BitsPointerMarked;
+}
+
+// When in GCmarkterminate phase we allocate black.
+void
+runtime·gcmarknewobject_m(void)
+{
+ Markbits mbits;
+ byte *obj;
+
+ if(runtime·gcphase != GCmarktermination)
+ runtime·throw("marking new object while not in mark termination phase");
+ if(checkmark) // The world should be stopped so this should not happen.
+ runtime·throw("gcmarknewobject called while doing checkmark");
+
+ obj = g->m->ptrarg[0];
+ slottombits((byte*)((uintptr)obj & (PtrSize-1)), &mbits);
+
+ if((mbits.bits&bitMarked) != 0)
+ return;
+
+ // Each byte of GC bitmap holds info for two words.
+ // If the current object is larger than two words, or if the object is one word
+ // but the object it shares the byte with is already marked,
+ // then all the possible concurrent updates are trying to set the same bit,
+ // so we can use a non-atomic update.
+ if((mbits.xbits&(bitMask|(bitMask<<gcBits))) != (bitBoundary|(bitBoundary<<gcBits)) || runtime·work.nproc == 1)
+ *mbits.bitp = mbits.xbits | (bitMarked<<mbits.shift);
+ else
+ runtime·atomicor8(mbits.bitp, bitMarked<<mbits.shift);
+ return;
+}
+
// obj is the start of an object with mark mbits.
// If it isn't already marked, mark it and enqueue into workbuf.
// Return possibly new workbuf to use.
@@ -343,21 +431,30 @@ greyobject(byte *obj, Markbits *mbits, Workbuf *wbuf)
if(((uintptr)obj & (PtrSize-1)) != 0)
runtime·throw("greyobject: obj not pointer-aligned");
- // If marked we have nothing to do.
- if((mbits->bits&bitMarked) != 0)
- return wbuf;
+ if(checkmark) {
+ if(!ismarked(mbits)) {
+ runtime·printf("runtime:greyobject: checkmarks finds unexpected unmarked object obj=%p, mbits->bits=%x, *mbits->bitp=%x\n", obj, mbits->bits, *mbits->bitp);
+ }
+ if(ischeckmarked(mbits))
+ return wbuf;
+ docheckmark(mbits);
+ } else {
+ // If marked we have nothing to do.
+ if((mbits->bits&bitMarked) != 0)
+ return wbuf;
+
+ // Each byte of GC bitmap holds info for two words.
+ // If the current object is larger than two words, or if the object is one word
+ // but the object it shares the byte with is already marked,
+ // then all the possible concurrent updates are trying to set the same bit,
+ // so we can use a non-atomic update.
+ if((mbits->xbits&(bitMask|(bitMask<<gcBits))) != (bitBoundary|(bitBoundary<<gcBits)) || runtime·work.nproc == 1)
+ *mbits->bitp = mbits->xbits | (bitMarked<<mbits->shift);
+ else
+ runtime·atomicor8(mbits->bitp, bitMarked<<mbits->shift);
+ }
- // Each byte of GC bitmap holds info for two words.
- // If the current object is larger than two words, or if the object is one word
- // but the object it shares the byte with is already marked,
- // then all the possible concurrent updates are trying to set the same bit,
- // so we can use a non-atomic update.
- if((mbits->xbits&(bitMask|(bitMask<<gcBits))) != (bitBoundary|(bitBoundary<<gcBits)) || runtime·work.nproc == 1)
- *mbits->bitp = mbits->xbits | (bitMarked<<mbits->shift);
- else
- runtime·atomicor8(mbits->bitp, bitMarked<<mbits->shift);
-
- if(((mbits->xbits>>(mbits->shift+2))&BitsMask) == BitsDead)
+ if (!checkmark && (((mbits->xbits>>(mbits->shift+2))&BitsMask) == BitsDead))
return wbuf; // noscan object
// Queue the obj for scanning. The PREFETCH(obj) logic has been removed but
@@ -398,6 +495,8 @@ scanobject(byte *b, uintptr n, byte *ptrmask, Workbuf *wbuf)
// Find bits of the beginning of the object.
if(ptrmask == nil) {
b = objectstart(b, &mbits);
+ if(b == nil)
+ return wbuf;
ptrbitp = mbits.bitp; //arena_start - off/wordsPerBitmapByte - 1;
}
for(i = 0; i < n; i += PtrSize) {
@@ -407,6 +506,7 @@ scanobject(byte *b, uintptr n, byte *ptrmask, Workbuf *wbuf)
bits = (ptrmask[(i/PtrSize)/4]>>(((i/PtrSize)%4)*BitsPerPointer))&BitsMask;
} else {
// Check if we have reached end of span.
+ // n is an overestimate of the size of the object.
if((((uintptr)b+i)%PageSize) == 0 &&
runtime·mheap.spans[(b-arena_start)>>PageShift] != runtime·mheap.spans[(b+i-arena_start)>>PageShift])
break;
@@ -414,7 +514,7 @@ scanobject(byte *b, uintptr n, byte *ptrmask, Workbuf *wbuf)
bits = *ptrbitp;
if(wordsPerBitmapByte != 2)
runtime·throw("alg doesn't work for wordsPerBitmapByte != 2");
- j = ((uintptr)b+i)/PtrSize & 1;
+ j = ((uintptr)b+i)/PtrSize & 1; // j indicates upper nibble or lower nibble
bits >>= gcBits*j;
if(i == 0)
bits &= ~bitBoundary;
@@ -422,15 +522,19 @@ scanobject(byte *b, uintptr n, byte *ptrmask, Workbuf *wbuf)
if((bits&bitBoundary) != 0 && i != 0)
break; // reached beginning of the next object
- bits = (bits>>2)&BitsMask;
- if(bits == BitsDead)
+ bits = (bits&bitPtrMask)>>2; // bits refer to the type bits.
+
+ if(i != 0 && bits == BitsDead) // BitsDead in first nibble not valid during checkmark
break; // reached no-scan part of the object
- }
+ }
- if(bits <= BitsScalar) // Bits Scalar || BitsDead
- continue;
- if(bits != BitsPointer) {
- runtime·printf("gc bits=%x\n", bits);
+ if(bits <= BitsScalar) // Bits Scalar ||
+ // BitsDead || // default encoding
+ // BitsScalarMarked // checkmark encoding
+ continue;
+
+ if((bits&BitsPointer) != BitsPointer) {
+ runtime·printf("gc checkmark=%d, b=%p ptrmask=%p, mbits.bitp=%p, mbits.xbits=%x, bits=%x\n", checkmark, b, ptrmask, mbits.bitp, mbits.xbits, bits);
runtime·throw("unexpected garbage collection bits");
}
@@ -442,6 +546,11 @@ scanobject(byte *b, uintptr n, byte *ptrmask, Workbuf *wbuf)
// Mark the object. return some important bits.
// We we combine the following two rotines we don't have to pass mbits or obj around.
obj = objectstart(obj, &mbits);
+ // In the case of the span being MSpan_Stack mbits is useless and will not have
+ // the boundary bit set. It does not need to be greyed since it will be
+ // scanned using the scan stack mechanism.
+ if(obj == nil)
+ continue;
wbuf = greyobject(obj, &mbits, wbuf);
}
return wbuf;
@@ -548,7 +657,8 @@ markroot(ParFor *desc, uint32 i)
s = runtime·work.spans[spanidx];
if(s->state != MSpanInUse)
continue;
- if(s->sweepgen != sg) {
+ if(!checkmark && s->sweepgen != sg) {
+ // sweepgen was updated (+2) during non-checkmark GC pass
runtime·printf("sweep %d %d\n", s->sweepgen, sg);
runtime·throw("gc: unswept span");
}
@@ -616,9 +726,6 @@ markroot(ParFor *desc, uint32 i)
}
}
-// wblock is used for creating new empty work buffer blocks.
-static Mutex wblock;
-
// Get an empty work buffer off the work.empty list,
// allocating new buffers as needed.
static Workbuf*
@@ -636,10 +743,8 @@ getempty(Workbuf *b)
runtime·throw("getempty: workbuffer not empty, b->nobj not 0");
}
if(b == nil) {
- runtime·lock(&wblock);
b = runtime·persistentalloc(sizeof(*b), CacheLineSize, &mstats.gc_sys);
b->nobj = 0;
- runtime·unlock(&wblock);
}
return b;
}
@@ -692,17 +797,6 @@ putpartial(Workbuf *b)
}
}
-void
-runtime·gcworkbuffree(Workbuf *b)
-{
- if(b == nil)
- return;
- if(b->nobj == 0)
- putempty(b);
- else
- putfull(b);
-}
-
// Get a full work buffer off the work.full or a partially
// filled one off the work.partial list. If nothing is available
// wait until all the other gc helpers have finished and then
@@ -906,11 +1000,18 @@ static bool
shaded(byte *slot)
{
Markbits mbits;
+ byte *valid;
if(!inheap(slot)) // non-heap slots considered grey
return true;
- objectstart(slot, &mbits);
+ valid = objectstart(slot, &mbits);
+ if(valid == nil)
+ return true;
+
+ if(checkmark)
+ return ischeckmarked(&mbits);
+
return (mbits.bits&bitMarked) != 0;
}
@@ -930,7 +1031,9 @@ shade(byte *b)
// Mark the object, return some important bits.
// If we combine the following two rotines we don't have to pass mbits or obj around.
obj = objectstart(b, &mbits);
- wbuf = greyobject(obj, &mbits, wbuf); // augments the wbuf
+ if(obj != nil)
+ wbuf = greyobject(obj, &mbits, wbuf); // augments the wbuf
+
putpartial(wbuf);
return;
}
@@ -969,6 +1072,7 @@ runtime·gcphasework(G *gp)
scanstack(gp);
break;
case GCmark:
+ break;
case GCmarktermination:
scanstack(gp);
// All available mark work will be emptied before returning.
@@ -1104,6 +1208,9 @@ runtime·MSpan_Sweep(MSpan *s, bool preserve)
Special *special, **specialp, *y;
bool res, sweepgenset;
+ if(checkmark)
+ runtime·throw("MSpan_Sweep: checkmark only runs in STW and after the sweep.");
+
// It's critical that we enter this function with preemption disabled,
// GC must not start while we are in the middle of this function.
if(g->m->locks == 0 && g->m->mallocing == 0 && g != g->m->g0)
@@ -1547,6 +1654,134 @@ runtime·gc_m(void)
runtime·casgstatus(gp, Gwaiting, Grunning);
}
+// Similar to clearcheckmarkbits but works on a single span.
+// It preforms two tasks.
+// 1. When used before the checkmark phase it converts BitsDead (00) to bitsScalar (01)
+// for nibbles with the BoundaryBit set.
+// 2. When used after the checkmark phase it converts BitsPointerMark (11) to BitsPointer 10 and
+// BitsScalarMark (00) to BitsScalar (01), thus clearing the checkmark mark encoding.
+// For the second case it is possible to restore the BitsDead pattern but since
+// clearmark is a debug tool performance has a lower priority than simplicity.
+// The span is MSpanInUse and the world is stopped.
+static void
+clearcheckmarkbitsspan(MSpan *s)
+{
+ int32 cl, n, npages, i;
+ uintptr size, off, step;
+ byte *p, *bitp, *arena_start, b;
+
+ if(!checkmark)
+ runtime·throw("clearcheckmarkbitsspan: checkmark not set.");
+
+ if(s->state != MSpanInUse) {
+ runtime·printf("runtime:clearcheckmarkbitsspan: state=%d\n",
+ s->state);
+ runtime·throw("clearcheckmarkbitsspan: bad span state");
+ }
+ arena_start = runtime·mheap.arena_start;
+ cl = s->sizeclass;
+ size = s->elemsize;
+ if(cl == 0) {
+ n = 1;
+ } else {
+ // Chunk full of small blocks.
+ npages = runtime·class_to_allocnpages[cl];
+ n = (npages << PageShift) / size;
+ }
+
+ // MSpan_Sweep has similar code but instead of overloading and
+ // complicating that routine we do a simpler walk here.
+ // Sweep through n objects of given size starting at p.
+ // This thread owns the span now, so it can manipulate
+ // the block bitmap without atomic operations.
+ p = (byte*)(s->start << PageShift);
+ // Find bits for the beginning of the span.
+ off = (uintptr*)p - (uintptr*)arena_start;
+ bitp = arena_start - off/wordsPerBitmapByte - 1;
+ step = size/(PtrSize*wordsPerBitmapByte);
+
+ if(step == 0) {
+ // updating top and bottom nibbles, all boundaries
+ for(i=0; i<n/2; i++, bitp--) {
+ if((*bitp & bitBoundary) != bitBoundary)
+ runtime·throw("missing bitBoundary");
+ b = (*bitp & bitPtrMask)>>2;
+ if(b == BitsScalarMarked || b == BitsPointerMarked)
+ *bitp ^= BitsCheckMarkXor<<2;
+
+ if(((*bitp>>gcBits) & bitBoundary) != bitBoundary)
+ runtime·throw("missing bitBoundary");
+ b = ((*bitp>>gcBits) & bitPtrMask)>>2;
+ if(b == BitsScalarMarked || b == BitsPointerMarked)
+ *bitp ^= BitsCheckMarkXor<<(2+gcBits);
+ }
+ } else {
+ // updating bottom nibble for first word of each object
+ for(i=0; i<n; i++, bitp -= step) {
+ if((*bitp & bitBoundary) != bitBoundary)
+ runtime·throw("missing bitBoundary");
+ b = (*bitp & bitPtrMask)>>2;
+ if(b == BitsScalarMarked || b == BitsPointerMarked)
+ *bitp ^= BitsCheckMarkXor<<2;
+ }
+ }
+}
+
+// clearcheckmarkbits preforms two tasks.
+// 1. When used before the checkmark phase it converts BitsDead (00) to bitsScalar (01)
+// for nibbles with the BoundaryBit set.
+// 2. When used after the checkmark phase it converts BitsPointerMark (11) to BitsPointer 10 and
+// BitsScalarMark (00) to BitsScalar (01), thus clearing the checkmark mark encoding.
+// This is a bit expensive but preserves the BitsDead encoding during the normal marking.
+// BitsDead remains valid for every nibble except the ones with BitsBoundary set.
+static void
+clearcheckmarkbits(void)
+{
+ uint32 idx;
+ MSpan *s;
+ for(idx=0; idx<runtime·work.nspan; idx++) {
+ s = runtime·work.spans[idx];
+ if(s->state == MSpanInUse) {
+ clearcheckmarkbitsspan(s);
+ }
+ }
+}
+
+// Called from malloc.go using onM.
+// The world is stopped. Rerun the scan and mark phases
+// using the bitMarkedCheck bit instead of the
+// bitMarked bit. If the marking encounters an
+// bitMarked bit that is not set then we throw.
+void
+runtime·gccheckmark_m(void)
+{
+ if(!gccheckmarkenable)
+ return;
+
+ if(checkmark)
+ runtime·throw("gccheckmark_m, entered with checkmark already true.");
+
+ checkmark = true;
+ clearcheckmarkbits(); // Converts BitsDead to BitsScalar.
+ runtime·gc_m();
+ // Work done, fixed up the GC bitmap to remove the checkmark bits.
+ clearcheckmarkbits();
+ checkmark = false;
+}
+
+// checkmarkenable is initially false
+void
+runtime·gccheckmarkenable_m(void)
+{
+ gccheckmarkenable = true;
+}
+
+void
+runtime·gccheckmarkdisable_m(void)
+{
+ gccheckmarkenable = false;
+}
+
void
runtime·finishsweep_m(void)
{
@@ -1631,6 +1866,21 @@ runtime·gcscan_m(void)
// Let the g that called us continue to run.
}
+// Mark all objects that are known about.
+void
+runtime·gcmark_m(void)
+{
+ scanblock(nil, 0, nil);
+}
+
+// For now this must be followed by a stoptheworld and a starttheworld to ensure
+// all go routines see the new barrier.
+void
+runtime·gcinstallmarkwb_m(void)
+{
+ runtime·gcphase = GCmark;
+}
+
static void
gc(struct gc_args *args)
{
@@ -1652,7 +1902,8 @@ gc(struct gc_args *args)
if(runtime·debug.gctrace)
t1 = runtime·nanotime();
- runtime·finishsweep_m();
+ if(!checkmark)
+ runtime·finishsweep_m(); // skip during checkmark debug phase.
// Cache runtime·mheap.allspans in work.spans to avoid conflicts with
// resizing/freeing allspans.
@@ -1676,7 +1927,7 @@ gc(struct gc_args *args)
runtime·work.nwait = 0;
runtime·work.ndone = 0;
runtime·work.nproc = runtime·gcprocs();
- runtime·gcphase = GCmark;
+ runtime·gcphase = GCmarktermination;
// World is stopped so allglen will not change.
for(i = 0; i < runtime·allglen; i++) {
@@ -1774,21 +2025,24 @@ gc(struct gc_args *args)
runtime·sweep.spanidx = 0;
runtime·unlock(&runtime·mheap.lock);
- if(ConcurrentSweep && !args->eagersweep) {
- runtime·lock(&runtime·gclock);
- if(runtime·sweep.g == nil)
- runtime·sweep.g = runtime·newproc1(&bgsweepv, nil, 0, 0, gc);
- else if(runtime·sweep.parked) {
- runtime·sweep.parked = false;
- runtime·ready(runtime·sweep.g);
+ // Start the sweep after the checkmark phase if there is one.
+ if(!gccheckmarkenable || checkmark) {
+ if(ConcurrentSweep && !args->eagersweep) {
+ runtime·lock(&runtime·gclock);
+ if(runtime·sweep.g == nil)
+ runtime·sweep.g = runtime·newproc1(&bgsweepv, nil, 0, 0, gc);
+ else if(runtime·sweep.parked) {
+ runtime·sweep.parked = false;
+ runtime·ready(runtime·sweep.g);
+ }
+ runtime·unlock(&runtime·gclock);
+ } else {
+ // Sweep all spans eagerly.
+ while(runtime·sweepone() != -1)
+ runtime·sweep.npausesweep++;
+ // Do an additional mProf_GC, because all 'free' events are now real as well.
+ runtime·mProf_GC();
}
- runtime·unlock(&runtime·gclock);
- } else {
- // Sweep all spans eagerly.
- while(runtime·sweepone() != -1)
- runtime·sweep.npausesweep++;
- // Do an additional mProf_GC, because all 'free' events are now real as well.
- runtime·mProf_GC();
}
runtime·mProf_GC();
diff --git a/src/runtime/mgc0.h b/src/runtime/mgc0.h
index 16fbe4665..519d7206e 100644
--- a/src/runtime/mgc0.h
+++ b/src/runtime/mgc0.h
@@ -45,8 +45,12 @@ enum {
// If you change these, also change scanblock.
// scanblock does "if(bits == BitsScalar || bits == BitsDead)" as "if(bits <= BitsScalar)".
BitsDead = 0,
- BitsScalar = 1,
- BitsPointer = 2,
+ BitsScalar = 1, // 01
+ BitsPointer = 2, // 10
+ BitsCheckMarkXor = 1, // 10
+ BitsScalarMarked = BitsScalar ^ BitsCheckMarkXor, // 00
+ BitsPointerMarked = BitsPointer ^ BitsCheckMarkXor, // 11
+
BitsMultiWord = 3,
// BitsMultiWord will be set for the first word of a multi-word item.
// When it is set, one of the following will be set for the second word.
diff --git a/src/runtime/stack.c b/src/runtime/stack.c
index f18171ea5..fb23cc1c3 100644
--- a/src/runtime/stack.c
+++ b/src/runtime/stack.c
@@ -382,8 +382,6 @@ adjustpointers(byte **scanp, BitVector *bv, AdjustInfo *adjinfo, Func *f)
uintptr delta;
int32 num, i;
byte *p, *minp, *maxp;
- Type *t;
- Itab *tab;
minp = (byte*)adjinfo->old.lo;
maxp = (byte*)adjinfo->old.hi;
@@ -415,43 +413,7 @@ adjustpointers(byte **scanp, BitVector *bv, AdjustInfo *adjinfo, Func *f)
}
break;
case BitsMultiWord:
- switch(bv->bytedata[(i+1) / (8 / BitsPerPointer)] >> ((i+1) * BitsPerPointer & 7) & 3) {
- default:
- runtime·throw("unexpected garbage collection bits");
- case BitsEface:
- t = (Type*)scanp[i];
- if(t != nil && ((t->kind & KindDirectIface) == 0 || (t->kind & KindNoPointers) == 0)) {
- p = scanp[i+1];
- if(minp <= p && p < maxp) {
- if(StackDebug >= 3)
- runtime·printf("adjust eface %p\n", p);
- if(t->size > PtrSize) // currently we always allocate such objects on the heap
- runtime·throw("large interface value found on stack");
- scanp[i+1] = p + delta;
- }
- }
- i++;
- break;
- case BitsIface:
- tab = (Itab*)scanp[i];
- if(tab != nil) {
- t = tab->type;
- //runtime·printf(" type=%p\n", t);
- if((t->kind & KindDirectIface) == 0 || (t->kind & KindNoPointers) == 0) {
- p = scanp[i+1];
- if(minp <= p && p < maxp) {
- if(StackDebug >= 3)
- runtime·printf("adjust iface %p\n", p);
- if(t->size > PtrSize) // currently we always allocate such objects on the heap
- runtime·throw("large interface value found on stack");
- scanp[i+1] = p + delta;
- }
- }
- }
- i++;
- break;
- }
- break;
+ runtime·throw("adjustpointers: unexpected garbage collection bits");
}
}
}
diff --git a/src/runtime/stubs.go b/src/runtime/stubs.go
index 2d5e41c1c..68f464f57 100644
--- a/src/runtime/stubs.go
+++ b/src/runtime/stubs.go
@@ -107,6 +107,12 @@ func mcacheRefill_m()
func largeAlloc_m()
func gc_m()
func gcscan_m()
+func gcmark_m()
+func gccheckmark_m()
+func gccheckmarkenable_m()
+func gccheckmarkdisable_m()
+func gcinstallmarkwb_m()
+func gcmarknewobject_m()
func finishsweep_m()
func scavenge_m()
func setFinalizer_m()