diff options
Diffstat (limited to 'src/pkg/runtime/mgc0.c')
| -rw-r--r-- | src/pkg/runtime/mgc0.c | 396 |
1 files changed, 273 insertions, 123 deletions
diff --git a/src/pkg/runtime/mgc0.c b/src/pkg/runtime/mgc0.c index 010f9cd96..f9dbdbb4a 100644 --- a/src/pkg/runtime/mgc0.c +++ b/src/pkg/runtime/mgc0.c @@ -18,6 +18,8 @@ enum { Debug = 0, DebugMark = 0, // run second pass to check mark CollectStats = 0, + ScanStackByFrames = 0, + IgnorePreciseGC = 0, // Four bits per word (see #defines below). wordsPerBitmapWord = sizeof(void*)*8/4, @@ -140,6 +142,7 @@ static Workbuf* getempty(Workbuf*); static Workbuf* getfull(Workbuf*); static void putempty(Workbuf*); static Workbuf* handoff(Workbuf*); +static void gchelperstart(void); static struct { uint64 full; // lock-free list of full blocks @@ -191,7 +194,7 @@ static struct { // markonly marks an object. It returns true if the object // has been marked by this function, false otherwise. -// This function isn't thread-safe and doesn't append the object to any buffer. +// This function doesn't append the object to any buffer. static bool markonly(void *obj) { @@ -254,13 +257,23 @@ found: // Only care about allocated and not marked. if((bits & (bitAllocated|bitMarked)) != bitAllocated) return false; - *bitp |= bitMarked<<shift; + if(work.nproc == 1) + *bitp |= bitMarked<<shift; + else { + for(;;) { + x = *bitp; + if(x & (bitMarked<<shift)) + return false; + if(runtime·casp((void**)bitp, (void*)x, (void*)(x|(bitMarked<<shift)))) + break; + } + } // The object is now marked return true; } -// PtrTarget and BitTarget are structures used by intermediate buffers. +// PtrTarget is a structure used by intermediate buffers. // The intermediate buffers hold GC data before it // is moved/flushed to the work buffer (Workbuf). // The size of an intermediate buffer is very small, @@ -272,25 +285,17 @@ struct PtrTarget uintptr ti; }; -typedef struct BitTarget BitTarget; -struct BitTarget -{ - void *p; - uintptr ti; - uintptr *bitp, shift; -}; - typedef struct BufferList BufferList; struct BufferList { PtrTarget ptrtarget[IntermediateBufferCapacity]; - BitTarget bittarget[IntermediateBufferCapacity]; Obj obj[IntermediateBufferCapacity]; - BufferList *next; + uint32 busy; + byte pad[CacheLineSize]; }; -static BufferList *bufferList; +#pragma dataflag 16 // no pointers +static BufferList bufferList[MaxGcproc]; -static Lock lock; static Type *itabtype; static void enqueue(Obj obj, Workbuf **_wbuf, Obj **_wp, uintptr *_nobj); @@ -301,7 +306,6 @@ static void enqueue(Obj obj, Workbuf **_wbuf, Obj **_wp, uintptr *_nobj); // and are prepared to be scanned by the garbage collector. // // _wp, _wbuf, _nobj are input/output parameters and are specifying the work buffer. -// bitbuf holds temporary data generated by this function. // // A simplified drawing explaining how the todo-list moves from a structure to another: // @@ -309,14 +313,12 @@ static void enqueue(Obj obj, Workbuf **_wbuf, Obj **_wp, uintptr *_nobj); // (find pointers) // Obj ------> PtrTarget (pointer targets) // ↑ | -// | | flushptrbuf (1st part, -// | | find block start) -// | ↓ -// `--------- BitTarget (pointer targets and the corresponding locations in bitmap) -// flushptrbuf -// (2nd part, mark and enqueue) +// | | +// `----------' +// flushptrbuf +// (find block start, mark and enqueue) static void -flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf, uintptr *_nobj, BitTarget *bitbuf) +flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf, uintptr *_nobj) { byte *p, *arena_start, *obj; uintptr size, *bitp, bits, shift, j, x, xbits, off, nobj, ti, n; @@ -325,7 +327,6 @@ flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf Obj *wp; Workbuf *wbuf; PtrTarget *ptrbuf_end; - BitTarget *bitbufpos, *bt; arena_start = runtime·mheap->arena_start; @@ -359,8 +360,6 @@ flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf { // Multi-threaded version. - bitbufpos = bitbuf; - while(ptrbuf < ptrbuf_end) { obj = ptrbuf->p; ti = ptrbuf->ti; @@ -438,26 +437,22 @@ flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf // Only care about allocated and not marked. if((bits & (bitAllocated|bitMarked)) != bitAllocated) continue; - - *bitbufpos++ = (BitTarget){obj, ti, bitp, shift}; - } - - runtime·lock(&lock); - for(bt=bitbuf; bt<bitbufpos; bt++){ - xbits = *bt->bitp; - bits = xbits >> bt->shift; - if((bits & bitMarked) != 0) - continue; - - // Mark the block - *bt->bitp = xbits | (bitMarked << bt->shift); + if(work.nproc == 1) + *bitp |= bitMarked<<shift; + else { + for(;;) { + x = *bitp; + if(x & (bitMarked<<shift)) + goto continue_obj; + if(runtime·casp((void**)bitp, (void*)x, (void*)(x|(bitMarked<<shift)))) + break; + } + } // If object has no pointers, don't need to scan further. if((bits & bitNoPointers) != 0) continue; - obj = bt->p; - // Ask span about size class. // (Manually inlined copy of MHeap_Lookup.) x = (uintptr)obj >> PageShift; @@ -467,11 +462,11 @@ flushptrbuf(PtrTarget *ptrbuf, PtrTarget **ptrbufpos, Obj **_wp, Workbuf **_wbuf PREFETCH(obj); - *wp = (Obj){obj, s->elemsize, bt->ti}; + *wp = (Obj){obj, s->elemsize, ti}; wp++; nobj++; + continue_obj:; } - runtime·unlock(&lock); // If another proc wants a pointer, give it some. if(work.nwait > 0 && nobj > handoffThreshold && work.full == 0) { @@ -559,6 +554,59 @@ struct Frame { uintptr *loop_or_ret; }; +// Sanity check for the derived type info objti. +static void +checkptr(void *obj, uintptr objti) +{ + uintptr *pc1, *pc2, type, tisize, i, j, x; + byte *objstart; + Type *t; + MSpan *s; + + if(!Debug) + runtime·throw("checkptr is debug only"); + + if(obj < runtime·mheap->arena_start || obj >= runtime·mheap->arena_used) + return; + type = runtime·gettype(obj); + t = (Type*)(type & ~(uintptr)(PtrSize-1)); + if(t == nil) + return; + x = (uintptr)obj >> PageShift; + if(sizeof(void*) == 8) + x -= (uintptr)(runtime·mheap->arena_start)>>PageShift; + s = runtime·mheap->map[x]; + objstart = (byte*)((uintptr)s->start<<PageShift); + if(s->sizeclass != 0) { + i = ((byte*)obj - objstart)/s->elemsize; + objstart += i*s->elemsize; + } + tisize = *(uintptr*)objti; + // Sanity check for object size: it should fit into the memory block. + if((byte*)obj + tisize > objstart + s->elemsize) + runtime·throw("invalid gc type info"); + if(obj != objstart) + return; + // If obj points to the beginning of the memory block, + // check type info as well. + if(t->string == nil || + // Gob allocates unsafe pointers for indirection. + (runtime·strcmp(t->string->str, (byte*)"unsafe.Pointer") && + // Runtime and gc think differently about closures. + runtime·strstr(t->string->str, (byte*)"struct { F uintptr") != t->string->str)) { + pc1 = (uintptr*)objti; + pc2 = (uintptr*)t->gc; + // A simple best-effort check until first GC_END. + for(j = 1; pc1[j] != GC_END && pc2[j] != GC_END; j++) { + if(pc1[j] != pc2[j]) { + runtime·printf("invalid gc type info for '%s' at %p, type info %p, block info %p\n", + t->string ? (int8*)t->string->str : (int8*)"?", j, pc1[j], pc2[j]); + runtime·throw("invalid gc type info"); + } + } + } +} + // scanblock scans a block of n bytes starting at pointer b for references // to other objects, scanning any it finds recursively until there are no // unscanned objects left. Instead of using an explicit recursion, it keeps @@ -575,20 +623,19 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) byte *b, *arena_start, *arena_used; uintptr n, i, end_b, elemsize, size, ti, objti, count, type; uintptr *pc, precise_type, nominal_size; - uintptr *map_ret, mapkey_size, mapval_size, mapkey_ti, mapval_ti; + uintptr *map_ret, mapkey_size, mapval_size, mapkey_ti, mapval_ti, *chan_ret; void *obj; Type *t; Slice *sliceptr; Frame *stack_ptr, stack_top, stack[GC_STACK_CAPACITY+4]; BufferList *scanbuffers; PtrTarget *ptrbuf, *ptrbuf_end, *ptrbufpos; - BitTarget *bitbuf; Obj *objbuf, *objbuf_end, *objbufpos; Eface *eface; Iface *iface; Hmap *hmap; MapType *maptype; - bool didmark, mapkey_kind, mapval_kind; + bool mapkey_kind, mapval_kind; struct hash_gciter map_iter; struct hash_gciter_data d; Hchan *chan; @@ -606,26 +653,13 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) precise_type = false; nominal_size = 0; - // Allocate ptrbuf, bitbuf + // Allocate ptrbuf { - runtime·lock(&lock); - - if(bufferList == nil) { - bufferList = runtime·SysAlloc(sizeof(*bufferList)); - if(bufferList == nil) - runtime·throw("runtime: cannot allocate memory"); - bufferList->next = nil; - } - scanbuffers = bufferList; - bufferList = bufferList->next; - + scanbuffers = &bufferList[m->helpgc]; ptrbuf = &scanbuffers->ptrtarget[0]; ptrbuf_end = &scanbuffers->ptrtarget[0] + nelem(scanbuffers->ptrtarget); - bitbuf = &scanbuffers->bittarget[0]; objbuf = &scanbuffers->obj[0]; objbuf_end = &scanbuffers->obj[0] + nelem(scanbuffers->obj); - - runtime·unlock(&lock); } ptrbufpos = ptrbuf; @@ -638,6 +672,7 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) mapkey_ti = mapval_ti = 0; chan = nil; chantype = nil; + chan_ret = nil; goto next_block; @@ -666,6 +701,17 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) } else { stack_top.count = 1; } + if(Debug) { + // Simple sanity check for provided type info ti: + // The declared size of the object must be not larger than the actual size + // (it can be smaller due to inferior pointers). + // It's difficult to make a comprehensive check due to inferior pointers, + // reflection, gob, etc. + if(pc[0] > n) { + runtime·printf("invalid gc type info: type info size %p, block size %p\n", pc[0], n); + runtime·throw("invalid gc type info"); + } + } } else if(UseSpanType) { if(CollectStats) runtime·xadd64(&gcstats.obj.notype, 1); @@ -703,7 +749,7 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) mapval_kind = maptype->elem->kind; mapval_ti = (uintptr)maptype->elem->gc | PRECISE; - map_ret = 0; + map_ret = nil; pc = mapProg; } else { goto next_block; @@ -712,6 +758,7 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) case TypeInfo_Chan: chan = (Hchan*)b; chantype = (ChanType*)t; + chan_ret = nil; pc = chanProg; break; default: @@ -725,6 +772,9 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) pc = defaultProg; } + if(IgnorePreciseGC) + pc = defaultProg; + pc++; stack_top.b = (uintptr)b; @@ -741,6 +791,8 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) obj = *(void**)(stack_top.b + pc[1]); objti = pc[2]; pc += 3; + if(Debug) + checkptr(obj, objti); break; case GC_SLICE: @@ -759,17 +811,29 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) case GC_STRING: obj = *(void**)(stack_top.b + pc[1]); + markonly(obj); pc += 2; - break; + continue; case GC_EFACE: eface = (Eface*)(stack_top.b + pc[1]); pc += 2; - if(eface->type != nil && (eface->data >= arena_start && eface->data < arena_used)) { - t = eface->type; + if(eface->type == nil) + continue; + + // eface->type + t = eface->type; + if((void*)t >= arena_start && (void*)t < arena_used) { + *ptrbufpos++ = (PtrTarget){t, 0}; + if(ptrbufpos == ptrbuf_end) + flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj); + } + + // eface->data + if(eface->data >= arena_start && eface->data < arena_used) { if(t->size <= sizeof(void*)) { if((t->kind & KindNoPointers)) - break; + continue; obj = eface->data; if((t->kind & ~KindNoPointers) == KindPtr) @@ -785,13 +849,13 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) iface = (Iface*)(stack_top.b + pc[1]); pc += 2; if(iface->tab == nil) - break; + continue; // iface->tab if((void*)iface->tab >= arena_start && (void*)iface->tab < arena_used) { *ptrbufpos++ = (PtrTarget){iface->tab, (uintptr)itabtype->gc}; if(ptrbufpos == ptrbuf_end) - flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj, bitbuf); + flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj); } // iface->data @@ -799,7 +863,7 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) t = iface->tab->type; if(t->size <= sizeof(void*)) { if((t->kind & KindNoPointers)) - break; + continue; obj = iface->data; if((t->kind & ~KindNoPointers) == KindPtr) @@ -812,13 +876,13 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) break; case GC_DEFAULT_PTR: - while((i = stack_top.b) <= end_b) { + while(stack_top.b <= end_b) { + obj = *(byte**)stack_top.b; stack_top.b += PtrSize; - obj = *(byte**)i; if(obj >= arena_start && obj < arena_used) { *ptrbufpos++ = (PtrTarget){obj, 0}; if(ptrbufpos == ptrbuf_end) - flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj, bitbuf); + flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj); } } goto next_block; @@ -826,9 +890,8 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) case GC_END: if(--stack_top.count != 0) { // Next iteration of a loop if possible. - elemsize = stack_top.elemsize; - stack_top.b += elemsize; - if(stack_top.b + elemsize <= end_b+PtrSize) { + stack_top.b += stack_top.elemsize; + if(stack_top.b + stack_top.elemsize <= end_b+PtrSize) { pc = stack_top.loop_or_ret; continue; } @@ -894,10 +957,7 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) pc += 3; continue; } - runtime·lock(&lock); - didmark = markonly(hmap); - runtime·unlock(&lock); - if(didmark) { + if(markonly(hmap)) { maptype = (MapType*)pc[2]; if(hash_gciter_init(hmap, &map_iter)) { mapkey_size = maptype->key->size; @@ -923,31 +983,41 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) while(hash_gciter_next(&map_iter, &d)) { // buffers: reserve space for 2 objects. if(ptrbufpos+2 >= ptrbuf_end) - flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj, bitbuf); + flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj); if(objbufpos+2 >= objbuf_end) flushobjbuf(objbuf, &objbufpos, &wp, &wbuf, &nobj); - if(d.st != nil) { - runtime·lock(&lock); + if(d.st != nil) markonly(d.st); - runtime·unlock(&lock); - } + if(d.key_data != nil) { if(!(mapkey_kind & KindNoPointers) || d.indirectkey) { if(!d.indirectkey) *objbufpos++ = (Obj){d.key_data, mapkey_size, mapkey_ti}; - else + else { + if(Debug) { + obj = *(void**)d.key_data; + if(!(arena_start <= obj && obj < arena_used)) + runtime·throw("scanblock: inconsistent hashmap"); + } *ptrbufpos++ = (PtrTarget){*(void**)d.key_data, mapkey_ti}; + } } if(!(mapval_kind & KindNoPointers) || d.indirectval) { if(!d.indirectval) *objbufpos++ = (Obj){d.val_data, mapval_size, mapval_ti}; - else + else { + if(Debug) { + obj = *(void**)d.val_data; + if(!(arena_start <= obj && obj < arena_used)) + runtime·throw("scanblock: inconsistent hashmap"); + } *ptrbufpos++ = (PtrTarget){*(void**)d.val_data, mapval_ti}; + } } } } - if(map_ret == 0) + if(map_ret == nil) goto next_block; pc = map_ret; continue; @@ -961,7 +1031,26 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) *objbufpos++ = (Obj){obj, size, objti}; if(objbufpos == objbuf_end) flushobjbuf(objbuf, &objbufpos, &wp, &wbuf, &nobj); - break; + continue; + + case GC_CHAN_PTR: + // Similar to GC_MAP_PTR + chan = *(Hchan**)(stack_top.b + pc[1]); + if(chan == nil) { + pc += 3; + continue; + } + if(markonly(chan)) { + chantype = (ChanType*)pc[2]; + if(!(chantype->elem->kind & KindNoPointers)) { + // Start chanProg. + chan_ret = pc+3; + pc = chanProg+1; + continue; + } + } + pc += 3; + continue; case GC_CHAN: // There are no heap pointers in struct Hchan, @@ -981,7 +1070,10 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) flushobjbuf(objbuf, &objbufpos, &wp, &wbuf, &nobj); } } - goto next_block; + if(chan_ret == nil) + goto next_block; + pc = chan_ret; + continue; default: runtime·throw("scanblock: invalid GC instruction"); @@ -991,7 +1083,7 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) if(obj >= arena_start && obj < arena_used) { *ptrbufpos++ = (PtrTarget){obj, objti}; if(ptrbufpos == ptrbuf_end) - flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj, bitbuf); + flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj); } } @@ -1000,7 +1092,7 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) // the loop by setting b, n, ti to the parameters for the next block. if(nobj == 0) { - flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj, bitbuf); + flushptrbuf(ptrbuf, &ptrbufpos, &wp, &wbuf, &nobj); flushobjbuf(objbuf, &objbufpos, &wp, &wbuf, &nobj); if(nobj == 0) { @@ -1026,11 +1118,7 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) nobj--; } -endscan: - runtime·lock(&lock); - scanbuffers->next = bufferList; - bufferList = scanbuffers; - runtime·unlock(&lock); +endscan:; } // debug_scanblock is the debug copy of scanblock. @@ -1299,51 +1387,94 @@ addroot(Obj obj) work.nroot++; } +// Scan a stack frame. The doframe parameter is a signal that the previously +// scanned activation has an unknown argument size. When *doframe is true the +// current activation must have its entire frame scanned. Otherwise, only the +// locals need to be scanned. +static void +addframeroots(Func *f, byte*, byte *sp, void *doframe) +{ + uintptr outs; + + if(thechar == '5') + sp += sizeof(uintptr); + if(f->locals == 0 || *(bool*)doframe == true) + addroot((Obj){sp, f->frame - sizeof(uintptr), 0}); + else if(f->locals > 0) { + outs = f->frame - sizeof(uintptr) - f->locals; + addroot((Obj){sp + outs, f->locals, 0}); + } + if(f->args > 0) + addroot((Obj){sp + f->frame, f->args, 0}); + *(bool*)doframe = (f->args == ArgsSizeUnknown); +} + static void addstackroots(G *gp) { M *mp; int32 n; Stktop *stk; - byte *sp, *guard; + byte *sp, *guard, *pc; + Func *f; + bool doframe; stk = (Stktop*)gp->stackbase; guard = (byte*)gp->stackguard; if(gp == g) { // Scanning our own stack: start at &gp. - sp = (byte*)&gp; + sp = runtime·getcallersp(&gp); + pc = runtime·getcallerpc(&gp); } else if((mp = gp->m) != nil && mp->helpgc) { // gchelper's stack is in active use and has no interesting pointers. return; + } else if(gp->gcstack != (uintptr)nil) { + // Scanning another goroutine that is about to enter or might + // have just exited a system call. It may be executing code such + // as schedlock and may have needed to start a new stack segment. + // Use the stack segment and stack pointer at the time of + // the system call instead, since that won't change underfoot. + sp = (byte*)gp->gcsp; + pc = gp->gcpc; + stk = (Stktop*)gp->gcstack; + guard = (byte*)gp->gcguard; } else { // Scanning another goroutine's stack. // The goroutine is usually asleep (the world is stopped). sp = (byte*)gp->sched.sp; - - // The exception is that if the goroutine is about to enter or might - // have just exited a system call, it may be executing code such - // as schedlock and may have needed to start a new stack segment. - // Use the stack segment and stack pointer at the time of - // the system call instead, since that won't change underfoot. - if(gp->gcstack != (uintptr)nil) { - stk = (Stktop*)gp->gcstack; - sp = (byte*)gp->gcsp; - guard = (byte*)gp->gcguard; + pc = gp->sched.pc; + if(ScanStackByFrames && pc == (byte*)runtime·goexit && gp->fnstart != nil) { + // The goroutine has not started. However, its incoming + // arguments are live at the top of the stack and must + // be scanned. No other live values should be on the + // stack. + f = runtime·findfunc((uintptr)gp->fnstart->fn); + if(f->args > 0) { + if(thechar == '5') + sp += sizeof(uintptr); + addroot((Obj){sp, f->args, 0}); + } + return; } } - - n = 0; - while(stk) { - if(sp < guard-StackGuard || (byte*)stk < sp) { - runtime·printf("scanstack inconsistent: g%D#%d sp=%p not in [%p,%p]\n", gp->goid, n, sp, guard-StackGuard, stk); - runtime·throw("scanstack"); + if (ScanStackByFrames) { + doframe = false; + runtime·gentraceback(pc, sp, nil, gp, 0, nil, 0x7fffffff, addframeroots, &doframe); + } else { + USED(pc); + n = 0; + while(stk) { + if(sp < guard-StackGuard || (byte*)stk < sp) { + runtime·printf("scanstack inconsistent: g%D#%d sp=%p not in [%p,%p]\n", gp->goid, n, sp, guard-StackGuard, stk); + runtime·throw("scanstack"); + } + addroot((Obj){sp, (byte*)stk - sp, (uintptr)defaultProg | PRECISE | LOOP}); + sp = (byte*)stk->gobuf.sp; + guard = stk->stackguard; + stk = (Stktop*)stk->stackbase; + n++; } - addroot((Obj){sp, (byte*)stk - sp, 0}); - sp = (byte*)stk->gobuf.sp; - guard = stk->stackguard; - stk = (Stktop*)stk->stackbase; - n++; } } @@ -1381,14 +1512,17 @@ addroots(void) for(spanidx=0; spanidx<runtime·mheap->nspan; spanidx++) { s = allspans[spanidx]; if(s->state == MSpanInUse) { + // The garbage collector ignores type pointers stored in MSpan.types: + // - Compiler-generated types are stored outside of heap. + // - The reflect package has runtime-generated types cached in its data structures. + // The garbage collector relies on finding the references via that cache. switch(s->types.compression) { case MTypes_Empty: case MTypes_Single: break; case MTypes_Words: case MTypes_Bytes: - // TODO(atom): consider using defaultProg instead of 0 - addroot((Obj){(byte*)&s->types.data, sizeof(void*), 0}); + markonly((byte*)s->types.data); break; } } @@ -1543,7 +1677,7 @@ sweepspan(ParFor *desc, uint32 idx) if(cl == 0) { // Free large span. runtime·unmarkspan(p, 1<<PageShift); - *(uintptr*)p = 1; // needs zeroing + *(uintptr*)p = (uintptr)0xdeaddeaddeaddeadll; // needs zeroing runtime·MHeap_Free(runtime·mheap, s, 1); c->local_alloc -= size; c->local_nfree++; @@ -1558,7 +1692,7 @@ sweepspan(ParFor *desc, uint32 idx) break; } if(size > sizeof(uintptr)) - ((uintptr*)p)[1] = 1; // mark as "needs to be zeroed" + ((uintptr*)p)[1] = (uintptr)0xdeaddeaddeaddeadll; // mark as "needs to be zeroed" end->next = (MLink*)p; end = (MLink*)p; @@ -1655,6 +1789,8 @@ runtime·memorydump(void) void runtime·gchelper(void) { + gchelperstart(); + // parallel mark for over gc roots runtime·parfordo(work.markfor); @@ -1668,6 +1804,7 @@ runtime·gchelper(void) } runtime·parfordo(work.sweepfor); + bufferList[m->helpgc].busy = 0; if(runtime·xadd(&work.ndone, +1) == work.nproc-1) runtime·notewakeup(&work.alldone); } @@ -1757,6 +1894,8 @@ runtime·gc(int32 force) // a problem in the past. if((((uintptr)&work.empty) & 7) != 0) runtime·throw("runtime: gc work buffer is misaligned"); + if((((uintptr)&work.full) & 7) != 0) + runtime·throw("runtime: gc work buffer is misaligned"); // The gc is turned off (via enablegc) until // the bootstrap has completed. @@ -1857,6 +1996,7 @@ gc(struct gc_args *args) t1 = runtime·nanotime(); + gchelperstart(); runtime·parfordo(work.markfor); scanblock(nil, nil, 0, true); @@ -1868,6 +2008,7 @@ gc(struct gc_args *args) t2 = runtime·nanotime(); runtime·parfordo(work.sweepfor); + bufferList[m->helpgc].busy = 0; t3 = runtime·nanotime(); if(work.nproc > 1) @@ -2009,6 +2150,15 @@ runtime∕debug·setGCPercent(intgo in, intgo out) } static void +gchelperstart(void) +{ + if(m->helpgc < 0 || m->helpgc >= MaxGcproc) + runtime·throw("gchelperstart: bad m->helpgc"); + if(runtime·xchg(&bufferList[m->helpgc].busy, 1)) + runtime·throw("gchelperstart: already busy"); +} + +static void runfinq(void) { Finalizer *f; |
