]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: precise garbage collection of channels
authorJan Ziak <0xe2.0x9a.0x9b@gmail.com>
Mon, 25 Feb 2013 20:58:23 +0000 (15:58 -0500)
committerRuss Cox <rsc@golang.org>
Mon, 25 Feb 2013 20:58:23 +0000 (15:58 -0500)
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

src/pkg/runtime/chan.c
src/pkg/runtime/malloc.h
src/pkg/runtime/mgc0.c
src/pkg/runtime/runtime.h

index a15b5d0d1af56743b393b2fff01f3002c3d58e85..32995c6ddd49d5859e15740efd1ad62ebdeb71e2 100644 (file)
@@ -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",
index c795a6fd5bd97bd9caf22b0fb7a3f78920368538..38122bf8a5e8b8f3d00ad2a1f7329f47b8a5ad13 100644 (file)
@@ -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,
index f6c76145a6025773a3c29ec93cf7c8efd4198dab..b2ed693c6599e7e3d4133a5d1a0b0514d992c7dd 100644 (file)
@@ -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;
index 61e33eb95e074989c868da27b514b589a062b183..6d7a3152ff56ed4faa54e6d1787619564d128e1b 100644 (file)
@@ -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