mirror of
https://github.com/golang/go
synced 2024-11-25 03:57:56 -07:00
runtime: correct memory leak in select
* adds pass 3 to dequeue from channels eagerly various other cleanup/churn: * use switch on cas->send in each pass to factor out common code. * longer goto labels, commented at target * be more agressive about can't happen: throw instead of print + cope. * use "select" instead of "selectgo" in errors * use printf for debug prints when possible R=ken2, ken3 CC=golang-dev, r https://golang.org/cl/875041
This commit is contained in:
parent
31693e9f14
commit
ec53627ed5
@ -79,6 +79,7 @@ struct Select
|
|||||||
Scase* scase[1]; // one per case
|
Scase* scase[1]; // one per case
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void dequeueg(WaitQ*, Hchan*);
|
||||||
static SudoG* dequeue(WaitQ*, Hchan*);
|
static SudoG* dequeue(WaitQ*, Hchan*);
|
||||||
static void enqueue(WaitQ*, SudoG*);
|
static void enqueue(WaitQ*, SudoG*);
|
||||||
static SudoG* allocsg(Hchan*);
|
static SudoG* allocsg(Hchan*);
|
||||||
@ -126,19 +127,9 @@ makechan(Type *elem, uint32 hint)
|
|||||||
c->dataqsiz = hint;
|
c->dataqsiz = hint;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(debug) {
|
if(debug)
|
||||||
prints("makechan: chan=");
|
printf("makechan: chan=%p; elemsize=%D; elemalg=%d; elemalign=%d; dataqsiz=%d\n",
|
||||||
·printpointer(c);
|
c, (int64)elem->size, elem->alg, elem->align, c->dataqsiz);
|
||||||
prints("; elemsize=");
|
|
||||||
·printint(elem->size);
|
|
||||||
prints("; elemalg=");
|
|
||||||
·printint(elem->alg);
|
|
||||||
prints("; elemalign=");
|
|
||||||
·printint(elem->align);
|
|
||||||
prints("; dataqsiz=");
|
|
||||||
·printint(c->dataqsiz);
|
|
||||||
prints("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
@ -175,7 +166,12 @@ incerr(Hchan* c)
|
|||||||
* occur. if pres is not nil,
|
* occur. if pres is not nil,
|
||||||
* then the protocol will not
|
* then the protocol will not
|
||||||
* sleep but return if it could
|
* sleep but return if it could
|
||||||
* not complete
|
* not complete.
|
||||||
|
*
|
||||||
|
* sleep can wake up with g->param == nil
|
||||||
|
* when a channel involved in the sleep has
|
||||||
|
* been closed. it is easiest to loop and re-run
|
||||||
|
* the operation; we'll see that it's now closed.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
chansend(Hchan *c, byte *ep, bool *pres)
|
chansend(Hchan *c, byte *ep, bool *pres)
|
||||||
@ -187,9 +183,7 @@ chansend(Hchan *c, byte *ep, bool *pres)
|
|||||||
gosched();
|
gosched();
|
||||||
|
|
||||||
if(debug) {
|
if(debug) {
|
||||||
prints("chansend: chan=");
|
printf("chansend: chan=%p; elem=", c);
|
||||||
·printpointer(c);
|
|
||||||
prints("; elem=");
|
|
||||||
c->elemalg->print(c->elemsize, ep);
|
c->elemalg->print(c->elemsize, ep);
|
||||||
prints("\n");
|
prints("\n");
|
||||||
}
|
}
|
||||||
@ -292,11 +286,8 @@ chanrecv(Hchan* c, byte *ep, bool* pres)
|
|||||||
if(gcwaiting)
|
if(gcwaiting)
|
||||||
gosched();
|
gosched();
|
||||||
|
|
||||||
if(debug) {
|
if(debug)
|
||||||
prints("chanrecv: chan=");
|
printf("chanrecv: chan=%p\n", c);
|
||||||
·printpointer(c);
|
|
||||||
prints("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
lock(c);
|
lock(c);
|
||||||
loop:
|
loop:
|
||||||
@ -471,13 +462,8 @@ void
|
|||||||
sel->tcase = size;
|
sel->tcase = size;
|
||||||
sel->ncase = 0;
|
sel->ncase = 0;
|
||||||
*selp = sel;
|
*selp = sel;
|
||||||
if(debug) {
|
if(debug)
|
||||||
prints("newselect s=");
|
printf("newselect s=%p size=%d\n", sel, size);
|
||||||
·printpointer(sel);
|
|
||||||
prints(" size=");
|
|
||||||
·printint(size);
|
|
||||||
prints("\n");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// selectsend(sel *byte, hchan *chan any, elem any) (selected bool);
|
// selectsend(sel *byte, hchan *chan any, elem any) (selected bool);
|
||||||
@ -511,19 +497,9 @@ void
|
|||||||
ae = (byte*)&sel + eo;
|
ae = (byte*)&sel + eo;
|
||||||
c->elemalg->copy(c->elemsize, cas->u.elem, ae);
|
c->elemalg->copy(c->elemsize, cas->u.elem, ae);
|
||||||
|
|
||||||
if(debug) {
|
if(debug)
|
||||||
prints("selectsend s=");
|
printf("selectsend s=%p pc=%p chan=%p so=%d send=%d\n",
|
||||||
·printpointer(sel);
|
sel, cas->pc, cas->chan, cas->so, cas->send);
|
||||||
prints(" pc=");
|
|
||||||
·printpointer(cas->pc);
|
|
||||||
prints(" chan=");
|
|
||||||
·printpointer(cas->chan);
|
|
||||||
prints(" so=");
|
|
||||||
·printint(cas->so);
|
|
||||||
prints(" send=");
|
|
||||||
·printint(cas->send);
|
|
||||||
prints("\n");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool);
|
// selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool);
|
||||||
@ -553,19 +529,9 @@ void
|
|||||||
cas->send = 0;
|
cas->send = 0;
|
||||||
cas->u.elemp = *(byte**)((byte*)&sel + eo);
|
cas->u.elemp = *(byte**)((byte*)&sel + eo);
|
||||||
|
|
||||||
if(debug) {
|
if(debug)
|
||||||
prints("selectrecv s=");
|
printf("selectrecv s=%p pc=%p chan=%p so=%d send=%d\n",
|
||||||
·printpointer(sel);
|
sel, cas->pc, cas->chan, cas->so, cas->send);
|
||||||
prints(" pc=");
|
|
||||||
·printpointer(cas->pc);
|
|
||||||
prints(" chan=");
|
|
||||||
·printpointer(cas->chan);
|
|
||||||
prints(" so=");
|
|
||||||
·printint(cas->so);
|
|
||||||
prints(" send=");
|
|
||||||
·printint(cas->send);
|
|
||||||
prints("\n");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -590,17 +556,9 @@ void
|
|||||||
cas->send = 2;
|
cas->send = 2;
|
||||||
cas->u.elemp = nil;
|
cas->u.elemp = nil;
|
||||||
|
|
||||||
if(debug) {
|
if(debug)
|
||||||
prints("selectdefault s=");
|
printf("selectdefault s=%p pc=%p so=%d send=%d\n",
|
||||||
·printpointer(sel);
|
sel, cas->pc, cas->so, cas->send);
|
||||||
prints(" pc=");
|
|
||||||
·printpointer(cas->pc);
|
|
||||||
prints(" so=");
|
|
||||||
·printint(cas->so);
|
|
||||||
prints(" send=");
|
|
||||||
·printint(cas->send);
|
|
||||||
prints("\n");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -657,15 +615,12 @@ void
|
|||||||
if(gcwaiting)
|
if(gcwaiting)
|
||||||
gosched();
|
gosched();
|
||||||
|
|
||||||
if(debug) {
|
if(debug)
|
||||||
prints("selectgo: sel=");
|
printf("select: sel=%p\n", sel);
|
||||||
·printpointer(sel);
|
|
||||||
prints("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(sel->ncase < 2) {
|
if(sel->ncase < 2) {
|
||||||
if(sel->ncase < 1)
|
if(sel->ncase < 1)
|
||||||
throw("selectgo: no cases");
|
throw("select: no cases");
|
||||||
// make special case of one.
|
// make special case of one.
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -674,9 +629,8 @@ void
|
|||||||
p = fastrand1();
|
p = fastrand1();
|
||||||
if(gcd(p, sel->ncase) == 1)
|
if(gcd(p, sel->ncase) == 1)
|
||||||
break;
|
break;
|
||||||
if(i > 1000) {
|
if(i > 1000)
|
||||||
throw("selectgo: failed to select prime");
|
throw("select: failed to select prime");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// select an initial offset
|
// select an initial offset
|
||||||
@ -700,43 +654,40 @@ loop:
|
|||||||
dfl = nil;
|
dfl = nil;
|
||||||
for(i=0; i<sel->ncase; i++) {
|
for(i=0; i<sel->ncase; i++) {
|
||||||
cas = sel->scase[o];
|
cas = sel->scase[o];
|
||||||
|
|
||||||
if(cas->send == 2) { // default
|
|
||||||
dfl = cas;
|
|
||||||
goto next1;
|
|
||||||
}
|
|
||||||
|
|
||||||
c = cas->chan;
|
c = cas->chan;
|
||||||
if(c->dataqsiz > 0) {
|
|
||||||
if(cas->send) {
|
|
||||||
if(c->closed & Wclosed)
|
|
||||||
goto sclose;
|
|
||||||
if(c->qcount < c->dataqsiz)
|
|
||||||
goto asyns;
|
|
||||||
goto next1;
|
|
||||||
}
|
|
||||||
if(c->qcount > 0)
|
|
||||||
goto asynr;
|
|
||||||
if(c->closed & Wclosed)
|
|
||||||
goto rclose;
|
|
||||||
goto next1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(cas->send) {
|
switch(cas->send) {
|
||||||
if(c->closed & Wclosed)
|
case 0: // recv
|
||||||
goto sclose;
|
if(c->dataqsiz > 0) {
|
||||||
sg = dequeue(&c->recvq, c);
|
if(c->qcount > 0)
|
||||||
if(sg != nil)
|
goto asyncrecv;
|
||||||
goto gots;
|
} else {
|
||||||
goto next1;
|
|
||||||
}
|
|
||||||
sg = dequeue(&c->sendq, c);
|
sg = dequeue(&c->sendq, c);
|
||||||
if(sg != nil)
|
if(sg != nil)
|
||||||
goto gotr;
|
goto syncrecv;
|
||||||
|
}
|
||||||
if(c->closed & Wclosed)
|
if(c->closed & Wclosed)
|
||||||
goto rclose;
|
goto rclose;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1: // send
|
||||||
|
if(c->closed & Wclosed)
|
||||||
|
goto sclose;
|
||||||
|
if(c->dataqsiz > 0) {
|
||||||
|
if(c->qcount < c->dataqsiz)
|
||||||
|
goto asyncsend;
|
||||||
|
} else {
|
||||||
|
sg = dequeue(&c->recvq, c);
|
||||||
|
if(sg != nil)
|
||||||
|
goto syncsend;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2: // default
|
||||||
|
dfl = cas;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
next1:
|
|
||||||
o += p;
|
o += p;
|
||||||
if(o >= sel->ncase)
|
if(o >= sel->ncase)
|
||||||
o -= sel->ncase;
|
o -= sel->ncase;
|
||||||
@ -752,52 +703,34 @@ loop:
|
|||||||
for(i=0; i<sel->ncase; i++) {
|
for(i=0; i<sel->ncase; i++) {
|
||||||
cas = sel->scase[o];
|
cas = sel->scase[o];
|
||||||
c = cas->chan;
|
c = cas->chan;
|
||||||
|
sg = allocsg(c);
|
||||||
|
sg->offset = o;
|
||||||
|
|
||||||
|
switch(cas->send) {
|
||||||
|
case 0: // recv
|
||||||
if(c->dataqsiz > 0) {
|
if(c->dataqsiz > 0) {
|
||||||
if(cas->send) {
|
if(c->qcount > 0)
|
||||||
if(c->qcount < c->dataqsiz) {
|
throw("select: pass 2 async recv");
|
||||||
prints("selectgo: pass 2 async send\n");
|
} else {
|
||||||
goto asyns;
|
if(dequeue(&c->sendq, c))
|
||||||
|
throw("select: pass 2 sync recv");
|
||||||
}
|
}
|
||||||
sg = allocsg(c);
|
|
||||||
sg->offset = o;
|
|
||||||
enqueue(&c->sendq, sg);
|
|
||||||
goto next2;
|
|
||||||
}
|
|
||||||
if(c->qcount > 0) {
|
|
||||||
prints("selectgo: pass 2 async recv\n");
|
|
||||||
goto asynr;
|
|
||||||
}
|
|
||||||
sg = allocsg(c);
|
|
||||||
sg->offset = o;
|
|
||||||
enqueue(&c->recvq, sg);
|
enqueue(&c->recvq, sg);
|
||||||
goto next2;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
if(cas->send) {
|
case 1: // send
|
||||||
sg = dequeue(&c->recvq, c);
|
if(c->dataqsiz > 0) {
|
||||||
if(sg != nil) {
|
if(c->qcount < c->dataqsiz)
|
||||||
prints("selectgo: pass 2 sync send\n");
|
throw("select: pass 2 async send");
|
||||||
g->selgen++;
|
} else {
|
||||||
goto gots;
|
if(dequeue(&c->recvq, c))
|
||||||
}
|
throw("select: pass 2 sync send");
|
||||||
sg = allocsg(c);
|
|
||||||
sg->offset = o;
|
|
||||||
c->elemalg->copy(c->elemsize, sg->elem, cas->u.elem);
|
c->elemalg->copy(c->elemsize, sg->elem, cas->u.elem);
|
||||||
|
}
|
||||||
enqueue(&c->sendq, sg);
|
enqueue(&c->sendq, sg);
|
||||||
goto next2;
|
break;
|
||||||
}
|
}
|
||||||
sg = dequeue(&c->sendq, c);
|
|
||||||
if(sg != nil) {
|
|
||||||
prints("selectgo: pass 2 sync recv\n");
|
|
||||||
g->selgen++;
|
|
||||||
goto gotr;
|
|
||||||
}
|
|
||||||
sg = allocsg(c);
|
|
||||||
sg->offset = o;
|
|
||||||
enqueue(&c->recvq, sg);
|
|
||||||
|
|
||||||
next2:
|
|
||||||
o += p;
|
o += p;
|
||||||
if(o >= sel->ncase)
|
if(o >= sel->ncase)
|
||||||
o -= sel->ncase;
|
o -= sel->ncase;
|
||||||
@ -810,6 +743,24 @@ loop:
|
|||||||
|
|
||||||
sellock(sel);
|
sellock(sel);
|
||||||
sg = g->param;
|
sg = g->param;
|
||||||
|
|
||||||
|
// pass 3 - dequeue from unsuccessful chans
|
||||||
|
// otherwise they stack up on quiet channels
|
||||||
|
for(i=0; i<sel->ncase; i++) {
|
||||||
|
if(sg == nil || o != sg->offset) {
|
||||||
|
cas = sel->scase[o];
|
||||||
|
c = cas->chan;
|
||||||
|
if(cas->send)
|
||||||
|
dequeueg(&c->sendq, c);
|
||||||
|
else
|
||||||
|
dequeueg(&c->recvq, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
o += p;
|
||||||
|
if(o >= sel->ncase)
|
||||||
|
o -= sel->ncase;
|
||||||
|
}
|
||||||
|
|
||||||
if(sg == nil)
|
if(sg == nil)
|
||||||
goto loop;
|
goto loop;
|
||||||
|
|
||||||
@ -822,19 +773,9 @@ loop:
|
|||||||
goto loop;
|
goto loop;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(debug) {
|
if(debug)
|
||||||
prints("wait-return: sel=");
|
printf("wait-return: sel=%p c=%p cas=%p send=%d o=%d\n",
|
||||||
·printpointer(sel);
|
sel, c, cas, cas->send, o);
|
||||||
prints(" c=");
|
|
||||||
·printpointer(c);
|
|
||||||
prints(" cas=");
|
|
||||||
·printpointer(cas);
|
|
||||||
prints(" send=");
|
|
||||||
·printint(cas->send);
|
|
||||||
prints(" o=");
|
|
||||||
·printint(o);
|
|
||||||
prints("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!cas->send) {
|
if(!cas->send) {
|
||||||
if(cas->u.elemp != nil)
|
if(cas->u.elemp != nil)
|
||||||
@ -844,7 +785,8 @@ loop:
|
|||||||
freesg(c, sg);
|
freesg(c, sg);
|
||||||
goto retc;
|
goto retc;
|
||||||
|
|
||||||
asynr:
|
asyncrecv:
|
||||||
|
// can receive from buffer
|
||||||
if(cas->u.elemp != nil)
|
if(cas->u.elemp != nil)
|
||||||
c->elemalg->copy(c->elemsize, cas->u.elemp, c->recvdataq->elem);
|
c->elemalg->copy(c->elemsize, cas->u.elemp, c->recvdataq->elem);
|
||||||
c->recvdataq = c->recvdataq->link;
|
c->recvdataq = c->recvdataq->link;
|
||||||
@ -857,7 +799,8 @@ asynr:
|
|||||||
}
|
}
|
||||||
goto retc;
|
goto retc;
|
||||||
|
|
||||||
asyns:
|
asyncsend:
|
||||||
|
// can send to buffer
|
||||||
if(cas->u.elem != nil)
|
if(cas->u.elem != nil)
|
||||||
c->elemalg->copy(c->elemsize, c->senddataq->elem, cas->u.elem);
|
c->elemalg->copy(c->elemsize, c->senddataq->elem, cas->u.elem);
|
||||||
c->senddataq = c->senddataq->link;
|
c->senddataq = c->senddataq->link;
|
||||||
@ -870,17 +813,10 @@ asyns:
|
|||||||
}
|
}
|
||||||
goto retc;
|
goto retc;
|
||||||
|
|
||||||
gotr:
|
syncrecv:
|
||||||
// recv path to wakeup the sender (sg)
|
// can receive from sleeping sender (sg)
|
||||||
if(debug) {
|
if(debug)
|
||||||
prints("gotr: sel=");
|
printf("syncrecv: sel=%p c=%p o=%d\n", sel, c, o);
|
||||||
·printpointer(sel);
|
|
||||||
prints(" c=");
|
|
||||||
·printpointer(c);
|
|
||||||
prints(" o=");
|
|
||||||
·printint(o);
|
|
||||||
prints("\n");
|
|
||||||
}
|
|
||||||
if(cas->u.elemp != nil)
|
if(cas->u.elemp != nil)
|
||||||
c->elemalg->copy(c->elemsize, cas->u.elemp, sg->elem);
|
c->elemalg->copy(c->elemsize, cas->u.elemp, sg->elem);
|
||||||
gp = sg->g;
|
gp = sg->g;
|
||||||
@ -889,23 +825,17 @@ gotr:
|
|||||||
goto retc;
|
goto retc;
|
||||||
|
|
||||||
rclose:
|
rclose:
|
||||||
|
// read at end of closed channel
|
||||||
if(cas->u.elemp != nil)
|
if(cas->u.elemp != nil)
|
||||||
c->elemalg->copy(c->elemsize, cas->u.elemp, nil);
|
c->elemalg->copy(c->elemsize, cas->u.elemp, nil);
|
||||||
c->closed |= Rclosed;
|
c->closed |= Rclosed;
|
||||||
incerr(c);
|
incerr(c);
|
||||||
goto retc;
|
goto retc;
|
||||||
|
|
||||||
gots:
|
syncsend:
|
||||||
// send path to wakeup the receiver (sg)
|
// can send to sleeping receiver (sg)
|
||||||
if(debug) {
|
if(debug)
|
||||||
prints("gots: sel=");
|
printf("syncsend: sel=%p c=%p o=%d\n", sel, c, o);
|
||||||
·printpointer(sel);
|
|
||||||
prints(" c=");
|
|
||||||
·printpointer(c);
|
|
||||||
prints(" o=");
|
|
||||||
·printint(o);
|
|
||||||
prints("\n");
|
|
||||||
}
|
|
||||||
if(c->closed & Wclosed)
|
if(c->closed & Wclosed)
|
||||||
goto sclose;
|
goto sclose;
|
||||||
c->elemalg->copy(c->elemsize, sg->elem, cas->u.elem);
|
c->elemalg->copy(c->elemsize, sg->elem, cas->u.elem);
|
||||||
@ -915,12 +845,14 @@ gots:
|
|||||||
goto retc;
|
goto retc;
|
||||||
|
|
||||||
sclose:
|
sclose:
|
||||||
|
// send on closed channel
|
||||||
incerr(c);
|
incerr(c);
|
||||||
goto retc;
|
goto retc;
|
||||||
|
|
||||||
retc:
|
retc:
|
||||||
selunlock(sel);
|
selunlock(sel);
|
||||||
|
|
||||||
|
// return to pc corresponding to chosen case
|
||||||
·setcallerpc(&sel, cas->pc);
|
·setcallerpc(&sel, cas->pc);
|
||||||
as = (byte*)&sel + cas->so;
|
as = (byte*)&sel + cas->so;
|
||||||
freesel(sel);
|
freesel(sel);
|
||||||
@ -1020,6 +952,20 @@ loop:
|
|||||||
return sgp;
|
return sgp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dequeueg(WaitQ *q, Hchan *c)
|
||||||
|
{
|
||||||
|
SudoG **l, *sgp;
|
||||||
|
|
||||||
|
for(l=&q->first; (sgp=*l) != nil; l=&sgp->link) {
|
||||||
|
if(sgp->g == g) {
|
||||||
|
*l = sgp->link;
|
||||||
|
freesg(c, sgp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
enqueue(WaitQ *q, SudoG *sgp)
|
enqueue(WaitQ *q, SudoG *sgp)
|
||||||
{
|
{
|
||||||
|
48
test/chan/select2.go
Normal file
48
test/chan/select2.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// $G $D/$F.go && $L $F.$A && ./$A.out
|
||||||
|
|
||||||
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "runtime"
|
||||||
|
|
||||||
|
func sender(c chan int, n int) {
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
c <- 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func receiver(c, dummy chan int, n int) {
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
select {
|
||||||
|
case <-c:
|
||||||
|
// nothing
|
||||||
|
case <-dummy:
|
||||||
|
panic("dummy")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
runtime.MemProfileRate = 0
|
||||||
|
|
||||||
|
c := make(chan int)
|
||||||
|
dummy := make(chan int)
|
||||||
|
|
||||||
|
// warm up
|
||||||
|
go sender(c, 100000)
|
||||||
|
receiver(c, dummy, 100000)
|
||||||
|
runtime.GC()
|
||||||
|
runtime.MemStats.Alloc = 0
|
||||||
|
|
||||||
|
// second time shouldn't increase footprint by much
|
||||||
|
go sender(c, 100000)
|
||||||
|
receiver(c, dummy, 100000)
|
||||||
|
runtime.GC()
|
||||||
|
|
||||||
|
if runtime.MemStats.Alloc > 1e5 {
|
||||||
|
println("BUG: too much memory for 100,000 selects:", runtime.MemStats.Alloc)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user