1
0
mirror of https://github.com/golang/go synced 2024-07-08 12:18:55 +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 \"\".mapiternext (hiter *any)\n"
"func \"\".mapiter1 (hiter *any) any\n" "func \"\".mapiter1 (hiter *any) any\n"
"func \"\".mapiter2 (hiter *any) (key any, val any)\n" "func \"\".mapiter2 (hiter *any) (key any, val any)\n"
"func \"\".makechan (elem *uint8, hint int64) chan any\n" "func \"\".makechan (chanType *uint8, hint int64) chan any\n"
"func \"\".chanrecv1 (hchan <-chan any) any\n" "func \"\".chanrecv1 (chanType *uint8, hchan <-chan any) any\n"
"func \"\".chanrecv2 (hchan <-chan any) (elem any, received bool)\n" "func \"\".chanrecv2 (chanType *uint8, hchan <-chan any) (elem any, received bool)\n"
"func \"\".chansend1 (hchan chan<- any, elem any)\n" "func \"\".chansend1 (chanType *uint8, hchan chan<- any, elem any)\n"
"func \"\".closechan (hchan any)\n" "func \"\".closechan (hchan any)\n"
"func \"\".closedchan (hchan any) bool\n" "func \"\".selectnbsend (chanType *uint8, hchan chan<- any, elem any) bool\n"
"func \"\".selectnbsend (hchan chan<- any, elem any) bool\n" "func \"\".selectnbrecv (chanType *uint8, elem *any, hchan <-chan any) bool\n"
"func \"\".selectnbrecv (elem *any, hchan <-chan any) bool\n" "func \"\".selectnbrecv2 (chanType *uint8, elem *any, received *bool, hchan <-chan any) bool\n"
"func \"\".selectnbrecv2 (elem *any, received *bool, hchan <-chan any) bool\n"
"func \"\".newselect (size int) *uint8\n" "func \"\".newselect (size int) *uint8\n"
"func \"\".selectsend (sel *uint8, hchan chan<- any, elem *any) bool\n" "func \"\".selectsend (sel *uint8, hchan chan<- any, elem *any) bool\n"
"func \"\".selectrecv (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) func mapiter2(hiter *any) (key any, val any)
// *byte is really *runtime.Type // *byte is really *runtime.Type
func makechan(elem *byte, hint int64) (hchan chan any) func makechan(chanType *byte, hint int64) (hchan chan any)
func chanrecv1(hchan <-chan any) (elem any) func chanrecv1(chanType *byte, hchan <-chan any) (elem any)
func chanrecv2(hchan <-chan any) (elem any, received bool) func chanrecv2(chanType *byte, hchan <-chan any) (elem any, received bool)
func chansend1(hchan chan<- any, elem any) func chansend1(chanType *byte, hchan chan<- any, elem any)
func closechan(hchan any) func closechan(hchan any)
func closedchan(hchan any) bool
func selectnbsend(hchan chan<- any, elem any) bool func selectnbsend(chanType *byte, hchan chan<- any, elem any) bool
func selectnbrecv(elem *any, hchan <-chan any) bool func selectnbrecv(chanType *byte, elem *any, hchan <-chan any) bool
func selectnbrecv2(elem *any, received *bool, hchan <-chan any) bool func selectnbrecv2(chanType *byte, elem *any, received *bool, hchan <-chan any) bool
func newselect(size int) (sel *byte) func newselect(size int) (sel *byte)
func selectsend(sel *byte, hchan chan<- any, elem *any) (selected bool) func selectsend(sel *byte, hchan chan<- any, elem *any) (selected bool)

View File

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

View File

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

View File

@ -1162,7 +1162,7 @@ func (iv internalValue) recv(nb bool) (val Value, ok bool) {
if ch == 0 { if ch == 0 {
panic("recv on nil channel") panic("recv on nil channel")
} }
valWord, selected, ok := chanrecv(ch, nb) valWord, selected, ok := chanrecv(iv.typ.runtimeType(), ch, nb)
if selected { if selected {
val = valueFromIword(0, t.Elem(), valWord) val = valueFromIword(0, t.Elem(), valWord)
} }
@ -1192,7 +1192,7 @@ func (iv internalValue) send(x Value, nb bool) (selected bool) {
if ch == 0 { if ch == 0 {
panic("send on nil channel") 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. // 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 chancap(ch iword) int32
func chanclose(ch iword) func chanclose(ch iword)
func chanlen(ch iword) int32 func chanlen(ch iword) int32
func chanrecv(ch iword, nb bool) (val iword, selected, received bool) func chanrecv(t *runtime.Type, ch iword, nb bool) (val iword, selected, received bool)
func chansend(ch iword, val iword, nb bool) bool func chansend(t *runtime.Type, ch iword, val iword, nb bool) bool
func makechan(typ *runtime.Type, size uint32) (ch iword) func makechan(typ *runtime.Type, size uint32) (ch iword)
func makemap(t *runtime.Type) iword func makemap(t *runtime.Type) iword

View File

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

View File

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

View File

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

View File

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