Commit 507df959 authored by Russ Cox's avatar Russ Cox

runtime: drop chan circular linked list in favor of circular buffer

The list elements are already being allocated out of a
single memory buffer.  We can drop the Link* pointer
following and the memory it requires, replacing it with
index operations.

The change also keeps a channel from containing a pointer
back into its own allocation block, which would create a
cycle.  Blocks involved in cycles are not guaranteed to be
finalized properly, and channels depend on finalizers to
free OS-level locks on some systems.  The self-reference
was keeping channels from being garbage collected.

runtime-gdb.py will need to be updated in order to dump
the content of buffered channels with the new data structure.

Fixes #1676.

R=ken2, r
CC=golang-dev
https://golang.org/cl/4411045
parent 4c006182
...@@ -1377,7 +1377,7 @@ static void ...@@ -1377,7 +1377,7 @@ static void
synthesizechantypes(DWDie *die) synthesizechantypes(DWDie *die)
{ {
DWDie *sudog, *waitq, *link, *hchan, DWDie *sudog, *waitq, *link, *hchan,
*dws, *dww, *dwl, *dwh, *elemtype; *dws, *dww, *dwh, *elemtype;
DWAttr *a; DWAttr *a;
int elemsize, linksize, sudogsize; int elemsize, linksize, sudogsize;
...@@ -1416,21 +1416,10 @@ synthesizechantypes(DWDie *die) ...@@ -1416,21 +1416,10 @@ synthesizechantypes(DWDie *die)
newattr(dww, DW_AT_byte_size, DW_CLS_CONSTANT, newattr(dww, DW_AT_byte_size, DW_CLS_CONSTANT,
getattr(waitq, DW_AT_byte_size)->value, NULL); getattr(waitq, DW_AT_byte_size)->value, NULL);
// link<T>
dwl = newdie(&dwtypes, DW_ABRV_STRUCTTYPE,
mkinternaltypename("link", getattr(elemtype, DW_AT_name)->data, NULL));
copychildren(dwl, link);
substitutetype(dwl, "link", defptrto(dwl));
substitutetype(dwl, "elem", elemtype);
newattr(dwl, DW_AT_byte_size, DW_CLS_CONSTANT,
linksize + (elemsize > 8 ? elemsize - 8 : 0), NULL);
// hchan<T> // hchan<T>
dwh = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, dwh = newdie(&dwtypes, DW_ABRV_STRUCTTYPE,
mkinternaltypename("hchan", getattr(elemtype, DW_AT_name)->data, NULL)); mkinternaltypename("hchan", getattr(elemtype, DW_AT_name)->data, NULL));
copychildren(dwh, hchan); copychildren(dwh, hchan);
substitutetype(dwh, "senddataq", defptrto(dwl));
substitutetype(dwh, "recvdataq", defptrto(dwl));
substitutetype(dwh, "recvq", dww); substitutetype(dwh, "recvq", dww);
substitutetype(dwh, "sendq", dww); substitutetype(dwh, "sendq", dww);
substitutetype(dwh, "free", dws); substitutetype(dwh, "free", dws);
......
...@@ -39,14 +39,18 @@ struct Hchan ...@@ -39,14 +39,18 @@ struct Hchan
bool closed; bool closed;
uint8 elemalign; uint8 elemalign;
Alg* elemalg; // interface for element type Alg* elemalg; // interface for element type
Link* senddataq; // pointer for sender uint32 sendx; // send index
Link* recvdataq; // pointer for receiver uint32 recvx; // receive index
WaitQ recvq; // list of recv waiters WaitQ recvq; // list of recv waiters
WaitQ sendq; // list of send waiters WaitQ sendq; // list of send waiters
SudoG* free; // freelist SudoG* free; // freelist
Lock; Lock;
}; };
// 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))
struct Link struct Link
{ {
Link* link; // asynch queue circular linked list Link* link; // asynch queue circular linked list
...@@ -97,8 +101,7 @@ Hchan* ...@@ -97,8 +101,7 @@ Hchan*
runtime·makechan_c(Type *elem, int64 hint) runtime·makechan_c(Type *elem, int64 hint)
{ {
Hchan *c; Hchan *c;
int32 i, m, n; int32 n;
Link *d, *b, *e;
byte *by; byte *by;
if(hint < 0 || (int32)hint != hint || hint > ((uintptr)-1) / elem->size) if(hint < 0 || (int32)hint != hint || hint > ((uintptr)-1) / elem->size)
...@@ -109,16 +112,13 @@ runtime·makechan_c(Type *elem, int64 hint) ...@@ -109,16 +112,13 @@ runtime·makechan_c(Type *elem, int64 hint)
runtime·throw("runtime.makechan: unsupported elem type"); runtime·throw("runtime.makechan: unsupported elem type");
} }
// calculate rounded sizes of Hchan and Link // calculate rounded size of Hchan
n = sizeof(*c); n = sizeof(*c);
while(n & MAXALIGN) while(n & MAXALIGN)
n++; n++;
m = sizeof(*d) + elem->size - sizeof(d->elem);
while(m & MAXALIGN)
m++;
// allocate memory in one call // allocate memory in one call
by = runtime·mal(n + hint*m); by = runtime·mal(n + hint*elem->size);
c = (Hchan*)by; c = (Hchan*)by;
by += n; by += n;
...@@ -127,26 +127,7 @@ runtime·makechan_c(Type *elem, int64 hint) ...@@ -127,26 +127,7 @@ runtime·makechan_c(Type *elem, int64 hint)
c->elemsize = elem->size; c->elemsize = elem->size;
c->elemalg = &runtime·algarray[elem->alg]; c->elemalg = &runtime·algarray[elem->alg];
c->elemalign = elem->align; c->elemalign = elem->align;
c->dataqsiz = hint;
if(hint > 0) {
// make a circular q
b = nil;
e = nil;
for(i=0; i<hint; i++) {
d = (Link*)by;
by += m;
if(e == nil)
e = d;
d->link = b;
b = d;
}
e->link = b;
c->recvdataq = b;
c->senddataq = b;
c->qcount = 0;
c->dataqsiz = hint;
}
if(debug) if(debug)
runtime·printf("makechan: chan=%p; elemsize=%D; elemalg=%d; elemalign=%d; dataqsiz=%d\n", runtime·printf("makechan: chan=%p; elemsize=%D; elemalg=%d; elemalign=%d; dataqsiz=%d\n",
...@@ -268,8 +249,9 @@ asynch: ...@@ -268,8 +249,9 @@ asynch:
goto asynch; goto asynch;
} }
if(ep != nil) if(ep != nil)
c->elemalg->copy(c->elemsize, c->senddataq->elem, ep); c->elemalg->copy(c->elemsize, chanbuf(c, c->sendx), ep);
c->senddataq = c->senddataq->link; if(++c->sendx == c->dataqsiz)
c->sendx = 0;
c->qcount++; c->qcount++;
sg = dequeue(&c->recvq, c); sg = dequeue(&c->recvq, c);
...@@ -380,9 +362,10 @@ asynch: ...@@ -380,9 +362,10 @@ asynch:
goto asynch; goto asynch;
} }
if(ep != nil) if(ep != nil)
c->elemalg->copy(c->elemsize, ep, c->recvdataq->elem); c->elemalg->copy(c->elemsize, ep, chanbuf(c, c->recvx));
c->elemalg->copy(c->elemsize, c->recvdataq->elem, nil); c->elemalg->copy(c->elemsize, chanbuf(c, c->recvx), nil);
c->recvdataq = c->recvdataq->link; if(++c->recvx == c->dataqsiz)
c->recvx = 0;
c->qcount--; c->qcount--;
sg = dequeue(&c->sendq, c); sg = dequeue(&c->sendq, c);
if(sg != nil) { if(sg != nil) {
...@@ -940,9 +923,10 @@ asyncrecv: ...@@ -940,9 +923,10 @@ asyncrecv:
if(cas->u.recv.receivedp != nil) if(cas->u.recv.receivedp != nil)
*cas->u.recv.receivedp = true; *cas->u.recv.receivedp = true;
if(cas->u.recv.elemp != nil) if(cas->u.recv.elemp != nil)
c->elemalg->copy(c->elemsize, cas->u.recv.elemp, c->recvdataq->elem); c->elemalg->copy(c->elemsize, cas->u.recv.elemp, chanbuf(c, c->recvx));
c->elemalg->copy(c->elemsize, c->recvdataq->elem, nil); c->elemalg->copy(c->elemsize, chanbuf(c, c->recvx), nil);
c->recvdataq = c->recvdataq->link; if(++c->recvx == c->dataqsiz)
c->recvx = 0;
c->qcount--; c->qcount--;
sg = dequeue(&c->sendq, c); sg = dequeue(&c->sendq, c);
if(sg != nil) { if(sg != nil) {
...@@ -955,8 +939,9 @@ asyncrecv: ...@@ -955,8 +939,9 @@ asyncrecv:
asyncsend: asyncsend:
// can send to buffer // 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, chanbuf(c, c->sendx), cas->u.elem);
c->senddataq = c->senddataq->link; if(++c->sendx == c->dataqsiz)
c->sendx = 0;
c->qcount++; c->qcount++;
sg = dequeue(&c->recvq, c); sg = dequeue(&c->recvq, c);
if(sg != nil) { if(sg != nil) {
......
// $G $D/$F.go && $L $F.$A && ./$A.out
// Copyright 2011 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.
// Check that buffered channels are garbage collected properly.
// An interesting case because they have finalizers and used to
// have self loops that kept them from being collected.
// (Cyclic data with finalizers is never finalized, nor collected.)
package main
import (
"fmt"
"os"
"runtime"
)
func main() {
const N = 10000
st := runtime.MemStats
for i := 0; i < N; i++ {
c := make(chan int, 10)
_ = c
if i%100 == 0 {
for j := 0; j < 4; j++ {
runtime.GC()
runtime.Gosched()
runtime.GC()
runtime.Gosched()
}
}
}
obj := runtime.MemStats.HeapObjects - st.HeapObjects
if obj > N/5 {
fmt.Println("too many objects left:", obj)
os.Exit(1)
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment