1
0
mirror of https://github.com/golang/go synced 2024-07-05 09:50:19 +00:00

gc: implement nil chan support

The spec has defined nil chans this way for months.
I'm behind.

R=ken2
CC=golang-dev
https://golang.org/cl/4897050
This commit is contained in:
Russ Cox 2011-08-17 15:54:17 -04:00
parent 65bde087ae
commit 3770b0e60c
9 changed files with 93 additions and 110 deletions

View File

@ -66,15 +66,14 @@ char *runtimeimport =
"func \"\".mapiternext (hiter *any)\n"
"func \"\".mapiter1 (hiter *any) any\n"
"func \"\".mapiter2 (hiter *any) (key any, val any)\n"
"func \"\".makechan (elem *uint8, hint int64) chan any\n"
"func \"\".chanrecv1 (hchan <-chan any) any\n"
"func \"\".chanrecv2 (hchan <-chan any) (elem any, received bool)\n"
"func \"\".chansend1 (hchan chan<- any, elem any)\n"
"func \"\".makechan (chanType *uint8, hint int64) chan any\n"
"func \"\".chanrecv1 (chanType *uint8, hchan <-chan any) any\n"
"func \"\".chanrecv2 (chanType *uint8, hchan <-chan any) (elem any, received bool)\n"
"func \"\".chansend1 (chanType *uint8, hchan chan<- any, elem any)\n"
"func \"\".closechan (hchan any)\n"
"func \"\".closedchan (hchan any) bool\n"
"func \"\".selectnbsend (hchan chan<- any, elem any) bool\n"
"func \"\".selectnbrecv (elem *any, hchan <-chan any) bool\n"
"func \"\".selectnbrecv2 (elem *any, received *bool, hchan <-chan any) bool\n"
"func \"\".selectnbsend (chanType *uint8, hchan chan<- any, elem any) bool\n"
"func \"\".selectnbrecv (chanType *uint8, elem *any, hchan <-chan any) bool\n"
"func \"\".selectnbrecv2 (chanType *uint8, elem *any, received *bool, hchan <-chan any) bool\n"
"func \"\".newselect (size int) *uint8\n"
"func \"\".selectsend (sel *uint8, hchan chan<- any, elem *any) bool\n"
"func \"\".selectrecv (sel *uint8, hchan <-chan any, elem *any) bool\n"

View File

@ -91,16 +91,15 @@ func mapiter1(hiter *any) (key any)
func mapiter2(hiter *any) (key any, val any)
// *byte is really *runtime.Type
func makechan(elem *byte, hint int64) (hchan chan any)
func chanrecv1(hchan <-chan any) (elem any)
func chanrecv2(hchan <-chan any) (elem any, received bool)
func chansend1(hchan chan<- any, elem any)
func makechan(chanType *byte, hint int64) (hchan chan any)
func chanrecv1(chanType *byte, hchan <-chan any) (elem any)
func chanrecv2(chanType *byte, hchan <-chan any) (elem any, received bool)
func chansend1(chanType *byte, hchan chan<- any, elem any)
func closechan(hchan any)
func closedchan(hchan any) bool
func selectnbsend(hchan chan<- any, elem any) bool
func selectnbrecv(elem *any, hchan <-chan any) bool
func selectnbrecv2(elem *any, received *bool, hchan <-chan any) bool
func selectnbsend(chanType *byte, hchan chan<- any, elem any) bool
func selectnbrecv(chanType *byte, elem *any, hchan <-chan any) bool
func selectnbrecv2(chanType *byte, elem *any, received *bool, hchan <-chan any) bool
func newselect(size int) (sel *byte)
func selectsend(sel *byte, hchan chan<- any, elem *any) (selected bool)

View File

@ -250,9 +250,8 @@ walkselect(Node *sel)
case OSEND:
// if c != nil && selectnbsend(c, v) { body } else { default body }
ch = cheapexpr(n->left, &r->ninit);
r->ntest = nod(OANDAND, nod(ONE, ch, nodnil()),
mkcall1(chanfn("selectnbsend", 2, ch->type),
types[TBOOL], &r->ninit, ch, n->right));
r->ntest = mkcall1(chanfn("selectnbsend", 2, ch->type),
types[TBOOL], &r->ninit, typename(ch->type), ch, n->right);
break;
case OSELRECV:
@ -260,9 +259,8 @@ walkselect(Node *sel)
r = nod(OIF, N, N);
r->ninit = cas->ninit;
ch = cheapexpr(n->right->left, &r->ninit);
r->ntest = nod(OANDAND, nod(ONE, ch, nodnil()),
mkcall1(chanfn("selectnbrecv", 2, ch->type),
types[TBOOL], &r->ninit, n->left, ch));
r->ntest = mkcall1(chanfn("selectnbrecv", 2, ch->type),
types[TBOOL], &r->ninit, typename(ch->type), n->left, ch);
break;
case OSELRECV2:
@ -270,9 +268,8 @@ walkselect(Node *sel)
r = nod(OIF, N, N);
r->ninit = cas->ninit;
ch = cheapexpr(n->right->left, &r->ninit);
r->ntest = nod(OANDAND, nod(ONE, ch, nodnil()),
mkcall1(chanfn("selectnbrecv2", 2, ch->type),
types[TBOOL], &r->ninit, n->left, n->ntest, ch));
r->ntest = mkcall1(chanfn("selectnbrecv2", 2, ch->type),
types[TBOOL], &r->ninit, typename(ch->type), n->left, n->ntest, ch);
break;
}
typecheck(&r->ntest, Erv);

View File

@ -591,7 +591,7 @@ walkexpr(Node **np, NodeList **init)
walkexprlistsafe(n->list, init);
walkexpr(&r->left, init);
fn = chanfn("chanrecv2", 2, r->left->type);
r = mkcall1(fn, getoutargx(fn->type), init, r->left);
r = mkcall1(fn, getoutargx(fn->type), init, typename(r->left->type), r->left);
n->rlist->n = r;
n->op = OAS2FUNC;
goto as2func;
@ -858,7 +858,7 @@ walkexpr(Node **np, NodeList **init)
case ORECV:
walkexpr(&n->left, init);
walkexpr(&n->right, init);
n = mkcall1(chanfn("chanrecv1", 2, n->left->type), n->type, init, n->left);
n = mkcall1(chanfn("chanrecv1", 2, n->left->type), n->type, init, typename(n->left->type), n->left);
goto ret;
case OSLICE:
@ -1078,7 +1078,7 @@ walkexpr(Node **np, NodeList **init)
case OMAKECHAN:
n = mkcall1(chanfn("makechan", 1, n->type), n->type, init,
typename(n->type->type),
typename(n->type),
conv(n->left, types[TINT64]));
goto ret;
@ -1163,7 +1163,7 @@ walkexpr(Node **np, NodeList **init)
goto ret;
case OSEND:
n = mkcall1(chanfn("chansend1", 2, n->left->type), T, init, n->left, n->right);
n = mkcall1(chanfn("chansend1", 2, n->left->type), T, init, typename(n->left->type), n->left, n->right);
goto ret;
case OCLOSURE:

View File

@ -1162,7 +1162,7 @@ func (iv internalValue) recv(nb bool) (val Value, ok bool) {
if ch == 0 {
panic("recv on nil channel")
}
valWord, selected, ok := chanrecv(ch, nb)
valWord, selected, ok := chanrecv(iv.typ.runtimeType(), ch, nb)
if selected {
val = valueFromIword(0, t.Elem(), valWord)
}
@ -1192,7 +1192,7 @@ func (iv internalValue) send(x Value, nb bool) (selected bool) {
if ch == 0 {
panic("send on nil channel")
}
return chansend(ch, ix.word, nb)
return chansend(iv.typ.runtimeType(), ch, ix.word, nb)
}
// Set assigns x to the value v.
@ -1720,8 +1720,8 @@ func convertForAssignment(what string, addr unsafe.Pointer, dst Type, iv interna
func chancap(ch iword) int32
func chanclose(ch iword)
func chanlen(ch iword) int32
func chanrecv(ch iword, nb bool) (val iword, selected, received bool)
func chansend(ch iword, val iword, nb bool) bool
func chanrecv(t *runtime.Type, ch iword, nb bool) (val iword, selected, received bool)
func chansend(t *runtime.Type, ch iword, val iword, nb bool) bool
func makechan(typ *runtime.Type, size uint32) (ch iword)
func makemap(t *runtime.Type) iword

View File

@ -81,10 +81,13 @@ static void enqueue(WaitQ*, SudoG*);
static void destroychan(Hchan*);
Hchan*
runtime·makechan_c(Type *elem, int64 hint)
runtime·makechan_c(ChanType *t, int64 hint)
{
Hchan *c;
int32 n;
Type *elem;
elem = t->elem;
if(hint < 0 || (int32)hint != hint || (elem->size > 0 && hint > ((uintptr)-1) / elem->size))
runtime·panicstring("makechan: size out of range");
@ -121,7 +124,7 @@ runtime·makechan_c(Type *elem, int64 hint)
void
reflect·makechan(ChanType *t, uint32 size, Hchan *c)
{
c = runtime·makechan_c(t->elem, size);
c = runtime·makechan_c(t, size);
FLUSH(&c);
}
@ -132,11 +135,11 @@ destroychan(Hchan *c)
}
// makechan(elem *Type, hint int64) (hchan *chan any);
// makechan(t *ChanType, hint int64) (hchan *chan any);
void
runtime·makechan(Type *elem, int64 hint, Hchan *ret)
runtime·makechan(ChanType *t, int64 hint, Hchan *ret)
{
ret = runtime·makechan_c(elem, hint);
ret = runtime·makechan_c(t, hint);
FLUSH(&ret);
}
@ -155,14 +158,22 @@ runtime·makechan(Type *elem, int64 hint, Hchan *ret)
* the operation; we'll see that it's now closed.
*/
void
runtime·chansend(Hchan *c, byte *ep, bool *pres)
runtime·chansend(ChanType *t, Hchan *c, byte *ep, bool *pres)
{
SudoG *sg;
SudoG mysg;
G* gp;
if(c == nil)
runtime·panicstring("send to nil channel");
if(c == nil) {
USED(t);
if(pres != nil) {
*pres = false;
return;
}
g->status = Gwaiting;
runtime·gosched();
return; // not reached
}
if(runtime·gcwaiting)
runtime·gosched();
@ -263,21 +274,29 @@ closed:
void
runtime·chanrecv(Hchan* c, byte *ep, bool *selected, bool *received)
runtime·chanrecv(ChanType *t, Hchan* c, byte *ep, bool *selected, bool *received)
{
SudoG *sg;
SudoG mysg;
G *gp;
if(c == nil)
runtime·panicstring("receive from nil channel");
if(runtime·gcwaiting)
runtime·gosched();
if(debug)
runtime·printf("chanrecv: chan=%p\n", c);
if(c == nil) {
USED(t);
if(selected != nil) {
*selected = false;
return;
}
g->status = Gwaiting;
runtime·gosched();
return; // not reached
}
runtime·lock(c);
if(c->dataqsiz > 0)
goto asynch;
@ -385,50 +404,29 @@ closed:
// chansend1(hchan *chan any, elem any);
#pragma textflag 7
void
runtime·chansend1(Hchan* c, ...)
runtime·chansend1(ChanType *t, Hchan* c, ...)
{
int32 o;
byte *ae;
if(c == nil)
runtime·panicstring("send to nil channel");
o = runtime·rnd(sizeof(c), c->elemalign);
ae = (byte*)&c + o;
runtime·chansend(c, ae, nil);
runtime·chansend(t, c, (byte*)(&c+1), nil);
}
// chanrecv1(hchan *chan any) (elem any);
#pragma textflag 7
void
runtime·chanrecv1(Hchan* c, ...)
runtime·chanrecv1(ChanType *t, Hchan* c, ...)
{
int32 o;
byte *ae;
o = runtime·rnd(sizeof(c), Structrnd);
ae = (byte*)&c + o;
runtime·chanrecv(c, ae, nil, nil);
runtime·chanrecv(t, c, (byte*)(&c+1), nil, nil);
}
// chanrecv2(hchan *chan any) (elem any, received bool);
#pragma textflag 7
void
runtime·chanrecv2(Hchan* c, ...)
runtime·chanrecv2(ChanType *t, Hchan* c, ...)
{
int32 o;
byte *ae, *ac;
if(c == nil)
runtime·panicstring("receive from nil channel");
byte *ae, *ap;
o = runtime·rnd(sizeof(c), Structrnd);
ae = (byte*)&c + o;
o += c->elemsize;
ac = (byte*)&c + o;
runtime·chanrecv(c, ae, nil, ac);
ae = (byte*)(&c+1);
ap = ae + t->elem->size;
runtime·chanrecv(t, c, ae, nil, ap);
}
// func selectnbsend(c chan any, elem any) bool
@ -444,7 +442,7 @@ runtime·chanrecv2(Hchan* c, ...)
//
// as
//
// if c != nil && selectnbsend(c, v) {
// if selectnbsend(c, v) {
// ... foo
// } else {
// ... bar
@ -452,17 +450,13 @@ runtime·chanrecv2(Hchan* c, ...)
//
#pragma textflag 7
void
runtime·selectnbsend(Hchan *c, ...)
runtime·selectnbsend(ChanType *t, Hchan *c, ...)
{
int32 o;
byte *ae, *ap;
o = runtime·rnd(sizeof(c), c->elemalign);
ae = (byte*)&c + o;
o = runtime·rnd(o+c->elemsize, Structrnd);
ap = (byte*)&c + o;
runtime·chansend(c, ae, ap);
ae = (byte*)(&c + 1);
ap = ae + runtime·rnd(t->elem->size, Structrnd);
runtime·chansend(t, c, ae, ap);
}
// func selectnbrecv(elem *any, c chan any) bool
@ -478,7 +472,7 @@ runtime·selectnbsend(Hchan *c, ...)
//
// as
//
// if c != nil && selectnbrecv(&v, c) {
// if selectnbrecv(&v, c) {
// ... foo
// } else {
// ... bar
@ -486,9 +480,9 @@ runtime·selectnbsend(Hchan *c, ...)
//
#pragma textflag 7
void
runtime·selectnbrecv(byte *v, Hchan *c, bool selected)
runtime·selectnbrecv(ChanType *t, byte *v, Hchan *c, bool selected)
{
runtime·chanrecv(c, v, &selected, nil);
runtime·chanrecv(t, c, v, &selected, nil);
}
// func selectnbrecv2(elem *any, ok *bool, c chan any) bool
@ -512,9 +506,9 @@ runtime·selectnbrecv(byte *v, Hchan *c, bool selected)
//
#pragma textflag 7
void
runtime·selectnbrecv2(byte *v, bool *received, Hchan *c, bool selected)
runtime·selectnbrecv2(ChanType *t, byte *v, bool *received, Hchan *c, bool selected)
{
runtime·chanrecv(c, v, &selected, received);
runtime·chanrecv(t, c, v, &selected, received);
}
// For reflect:
@ -525,14 +519,11 @@ runtime·selectnbrecv2(byte *v, bool *received, Hchan *c, bool selected)
// The "uintptr selected" is really "bool selected" but saying
// uintptr gets us the right alignment for the output parameter block.
void
reflect·chansend(Hchan *c, uintptr val, bool nb, uintptr selected)
reflect·chansend(ChanType *t, Hchan *c, uintptr val, bool nb, uintptr selected)
{
bool *sp;
byte *vp;
if(c == nil)
runtime·panicstring("send to nil channel");
if(nb) {
selected = false;
sp = (bool*)&selected;
@ -541,11 +532,11 @@ reflect·chansend(Hchan *c, uintptr val, bool nb, uintptr selected)
FLUSH(&selected);
sp = nil;
}
if(c->elemsize <= sizeof(val))
if(t->elem->size <= sizeof(val))
vp = (byte*)&val;
else
vp = (byte*)val;
runtime·chansend(c, vp, sp);
runtime·chansend(t, c, vp, sp);
}
// For reflect:
@ -553,13 +544,10 @@ reflect·chansend(Hchan *c, uintptr val, bool nb, uintptr selected)
// where an iword is the same word an interface value would use:
// the actual data if it fits, or else a pointer to the data.
void
reflect·chanrecv(Hchan *c, bool nb, uintptr val, bool selected, bool received)
reflect·chanrecv(ChanType *t, Hchan *c, bool nb, uintptr val, bool selected, bool received)
{
byte *vp;
bool *sp;
if(c == nil)
runtime·panicstring("receive from nil channel");
if(nb) {
selected = false;
@ -571,15 +559,15 @@ reflect·chanrecv(Hchan *c, bool nb, uintptr val, bool selected, bool received)
}
received = false;
FLUSH(&received);
if(c->elemsize <= sizeof(val)) {
if(t->elem->size <= sizeof(val)) {
val = 0;
vp = (byte*)&val;
} else {
vp = runtime·mal(c->elemsize);
vp = runtime·mal(t->elem->size);
val = (uintptr)vp;
FLUSH(&val);
}
runtime·chanrecv(c, vp, sp, &received);
runtime·chanrecv(t, c, vp, sp, &received);
}
static void newselect(int32, Select**);

View File

@ -62,6 +62,7 @@ typedef struct Iface Iface;
typedef struct Itab Itab;
typedef struct Eface Eface;
typedef struct Type Type;
typedef struct ChanType ChanType;
typedef struct MapType MapType;
typedef struct Defer Defer;
typedef struct Panic Panic;
@ -624,9 +625,9 @@ bool runtime·mapiterkey(struct hash_iter*, void*);
void runtime·mapiterkeyvalue(struct hash_iter*, void*, void*);
Hmap* runtime·makemap_c(MapType*, int64);
Hchan* runtime·makechan_c(Type*, int64);
void runtime·chansend(Hchan*, void*, bool*);
void runtime·chanrecv(Hchan*, void*, bool*, bool*);
Hchan* runtime·makechan_c(ChanType*, int64);
void runtime·chansend(ChanType*, Hchan*, void*, bool*);
void runtime·chanrecv(ChanType*, Hchan*, void*, bool*, bool*);
int32 runtime·chanlen(Hchan*);
int32 runtime·chancap(Hchan*);

View File

@ -16,7 +16,6 @@ typedef struct UncommonType UncommonType;
typedef struct InterfaceType InterfaceType;
typedef struct Method Method;
typedef struct IMethod IMethod;
typedef struct ChanType ChanType;
typedef struct SliceType SliceType;
typedef struct FuncType FuncType;

View File

@ -58,15 +58,15 @@ func main() {
closedch := make(chan int)
close(closedch)
// sending/receiving from a nil channel outside a select panics
testPanic(always, func() {
// sending/receiving from a nil channel blocks
testBlock(always, func() {
nilch <- 7
})
testPanic(always, func() {
testBlock(always, func() {
<-nilch
})
// sending/receiving from a nil channel inside a select never panics
// sending/receiving from a nil channel inside a select is never selected
testPanic(never, func() {
select {
case nilch <- 7: