aboutsummaryrefslogtreecommitdiff
path: root/src/pkg/runtime
diff options
context:
space:
mode:
authorJan Ziak <0xe2.0x9a.0x9b@gmail.com>2013-02-25 15:58:23 -0500
committerRuss Cox <rsc@golang.org>2013-02-25 15:58:23 -0500
commita656f82071c1631ed0aae5c403cf948fc06b52ce (patch)
tree02659c81c4c519260fb6a22b65be44429d36f3e5 /src/pkg/runtime
parent707ab1347f114934d65b713e22fdd62b4a83ca36 (diff)
downloadgo-a656f82071c1631ed0aae5c403cf948fc06b52ce.tar.xz
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
Diffstat (limited to 'src/pkg/runtime')
-rw-r--r--src/pkg/runtime/chan.c5
-rw-r--r--src/pkg/runtime/malloc.h1
-rw-r--r--src/pkg/runtime/mgc0.c33
-rw-r--r--src/pkg/runtime/runtime.h1
4 files changed, 40 insertions, 0 deletions
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