From: Jan Ziak <0xe2.0x9a.0x9b@gmail.com> Date: Mon, 25 Feb 2013 20:58:23 +0000 (-0500) Subject: runtime: precise garbage collection of channels X-Git-Tag: go1.1rc2~859 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=a656f82071c1631ed0aae5c403cf948fc06b52ce;p=gostls13.git runtime: precise garbage collection of channels This changeset adds a mostly-precise garbage collection of channels. The garbage collection support code in the linker isn't recognizing channel types yet. Fixes issue http://stackoverflow.com/questions/14712586/memory-consumption-skyrocket R=dvyukov, rsc, bradfitz CC=dave, golang-dev, minux.ma, remyoudompheng https://golang.org/cl/7307086 --- diff --git a/src/pkg/runtime/chan.c b/src/pkg/runtime/chan.c index a15b5d0d1a..32995c6ddd 100644 --- a/src/pkg/runtime/chan.c +++ b/src/pkg/runtime/chan.c @@ -33,6 +33,8 @@ struct WaitQ SudoG* last; }; +// The garbage collector is assuming that Hchan can only contain pointers into the stack +// and cannot contain pointers into the heap. struct Hchan { uintgo qcount; // total data in the q @@ -48,6 +50,8 @@ struct Hchan Lock; }; +uint32 runtime·Hchansize = sizeof(Hchan); + // Buffer follows Hchan immediately in memory. // chanbuf(c, i) is pointer to the i'th slot in the buffer. #define chanbuf(c, i) ((byte*)((c)+1)+(uintptr)(c)->elemsize*(i)) @@ -112,6 +116,7 @@ runtime·makechan_c(ChanType *t, int64 hint) c->elemalg = elem->alg; c->elemalign = elem->align; c->dataqsiz = hint; + runtime·settype(c, (uintptr)t | TypeInfo_Chan); if(debug) runtime·printf("makechan: chan=%p; elemsize=%D; elemalg=%p; elemalign=%d; dataqsiz=%D\n", diff --git a/src/pkg/runtime/malloc.h b/src/pkg/runtime/malloc.h index c795a6fd5b..38122bf8a5 100644 --- a/src/pkg/runtime/malloc.h +++ b/src/pkg/runtime/malloc.h @@ -482,6 +482,7 @@ enum TypeInfo_SingleObject = 0, TypeInfo_Array = 1, TypeInfo_Map = 2, + TypeInfo_Chan = 3, // Enables type information at the end of blocks allocated from heap DebugTypeAtBlockEnd = 0, diff --git a/src/pkg/runtime/mgc0.c b/src/pkg/runtime/mgc0.c index f6c76145a6..b2ed693c65 100644 --- a/src/pkg/runtime/mgc0.c +++ b/src/pkg/runtime/mgc0.c @@ -164,6 +164,7 @@ static struct { enum { GC_DEFAULT_PTR = GC_NUM_INSTR, GC_MAP_NEXT, + GC_CHAN, }; // markonly marks an object. It returns true if the object @@ -521,6 +522,9 @@ static uintptr defaultProg[2] = {PtrSize, GC_DEFAULT_PTR}; // Hashmap iterator program static uintptr mapProg[2] = {0, GC_MAP_NEXT}; +// Hchan program +static uintptr chanProg[2] = {0, GC_CHAN}; + // Local variables of a program fragment or loop typedef struct Frame Frame; struct Frame { @@ -560,6 +564,8 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) bool didmark, mapkey_kind, mapval_kind; struct hash_gciter map_iter; struct hash_gciter_data d; + Hchan *chan; + ChanType *chantype; if(sizeof(Workbuf) % PageSize != 0) runtime·throw("scanblock: size of Workbuf is suboptimal"); @@ -601,6 +607,8 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) mapkey_size = mapval_size = 0; mapkey_kind = mapval_kind = false; mapkey_ti = mapval_ti = 0; + chan = nil; + chantype = nil; goto next_block; @@ -660,6 +668,11 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) goto next_block; } break; + case TypeInfo_Chan: + chan = (Hchan*)b; + chantype = (ChanType*)t; + pc = chanProg; + break; default: runtime·throw("scanblock: invalid type"); return; @@ -897,6 +910,26 @@ scanblock(Workbuf *wbuf, Obj *wp, uintptr nobj, bool keepworking) pc += 4; break; + case GC_CHAN: + // There are no heap pointers in struct Hchan, + // so we can ignore the leading sizeof(Hchan) bytes. + if(!(chantype->elem->kind & KindNoPointers)) { + // Channel's buffer follows Hchan immediately in memory. + // Size of buffer (cap(c)) is second int in the chan struct. + n = ((uintgo*)chan)[1]; + if(n > 0) { + // TODO(atom): split into two chunks so that only the + // in-use part of the circular buffer is scanned. + // (Channel routines zero the unused part, so the current + // code does not lead to leaks, it's just a little inefficient.) + *objbufpos++ = (Obj){(byte*)chan+runtime·Hchansize, n*chantype->elem->size, + (uintptr)chantype->elem->gc | PRECISE | LOOP}; + if(objbufpos == objbuf_end) + flushobjbuf(objbuf, &objbufpos, &wp, &wbuf, &nobj); + } + } + goto next_block; + default: runtime·throw("scanblock: invalid GC instruction"); return; diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h index 61e33eb95e..6d7a3152ff 100644 --- a/src/pkg/runtime/runtime.h +++ b/src/pkg/runtime/runtime.h @@ -609,6 +609,7 @@ extern int32 runtime·ncpu; extern bool runtime·iscgo; extern void (*runtime·sysargs)(int32, uint8**); extern uint32 runtime·maxstring; +extern uint32 runtime·Hchansize; /* * common functions and data