add test for close/closed, fix a few implementation bugs.

R=ken
OCL=26664
CL=26664
This commit is contained in:
Russ Cox 2009-03-23 18:50:35 -07:00
parent 86145611b0
commit 13584f4a23
2 changed files with 227 additions and 28 deletions

View file

@ -176,13 +176,13 @@ sendchan(Hchan *c, byte *ep, bool *pres)
} }
lock(&chanlock); lock(&chanlock);
loop:
if(c->closed & Wclosed)
goto closed;
if(c->dataqsiz > 0) if(c->dataqsiz > 0)
goto asynch; goto asynch;
if(c->closed & Wclosed)
goto closed;
sg = dequeue(&c->recvq, c); sg = dequeue(&c->recvq, c);
if(sg != nil) { if(sg != nil) {
if(ep != nil) if(ep != nil)
@ -215,6 +215,8 @@ sendchan(Hchan *c, byte *ep, bool *pres)
lock(&chanlock); lock(&chanlock);
sg = g->param; sg = g->param;
if(sg == nil)
goto loop;
freesg(c, sg); freesg(c, sg);
unlock(&chanlock); unlock(&chanlock);
if(pres != nil) if(pres != nil)
@ -260,7 +262,7 @@ asynch:
closed: closed:
incerr(c); incerr(c);
if(pres != nil) if(pres != nil)
*pres = false; *pres = true;
unlock(&chanlock); unlock(&chanlock);
} }
@ -277,6 +279,7 @@ chanrecv(Hchan* c, byte *ep, bool* pres)
} }
lock(&chanlock); lock(&chanlock);
loop:
if(c->dataqsiz > 0) if(c->dataqsiz > 0)
goto asynch; goto asynch;
@ -312,11 +315,8 @@ chanrecv(Hchan* c, byte *ep, bool* pres)
lock(&chanlock); lock(&chanlock);
sg = g->param; sg = g->param;
if(sg == nil)
if(c->closed & Wclosed) { goto loop;
freesg(c, sg);
goto closed;
}
c->elemalg->copy(c->elemsize, ep, sg->elem); c->elemalg->copy(c->elemsize, ep, sg->elem);
freesg(c, sg); freesg(c, sg);
@ -368,7 +368,7 @@ closed:
c->closed |= Rclosed; c->closed |= Rclosed;
incerr(c); incerr(c);
if(pres != nil) if(pres != nil)
*pres = false; *pres = true;
unlock(&chanlock); unlock(&chanlock);
} }
@ -651,32 +651,32 @@ loop:
c = cas->chan; c = cas->chan;
if(c->dataqsiz > 0) { if(c->dataqsiz > 0) {
if(cas->send) { if(cas->send) {
if(c->closed & Wclosed)
goto sclose;
if(c->qcount < c->dataqsiz) if(c->qcount < c->dataqsiz)
goto asyns; goto asyns;
if(c->closed & Wclosed)
goto gots;
goto next1; goto next1;
} }
if(c->qcount > 0) if(c->qcount > 0)
goto asynr; goto asynr;
if(c->closed & Wclosed) if(c->closed & Wclosed)
goto gotr; goto rclose;
goto next1; goto next1;
} }
if(cas->send) { if(cas->send) {
if(c->closed & Wclosed)
goto sclose;
sg = dequeue(&c->recvq, c); sg = dequeue(&c->recvq, c);
if(sg != nil) if(sg != nil)
goto gots; goto gots;
if(c->closed & Wclosed)
goto gots;
goto next1; goto next1;
} }
sg = dequeue(&c->sendq, c); sg = dequeue(&c->sendq, c);
if(sg != nil) if(sg != nil)
goto gotr; goto gotr;
if(c->closed & Wclosed) if(c->closed & Wclosed)
goto gotr; goto rclose;
next1: next1:
o += p; o += p;
@ -823,13 +823,6 @@ gotr:
sys·printint(o); sys·printint(o);
prints("\n"); prints("\n");
} }
if(c->closed & Wclosed) {
if(cas->u.elemp != nil)
c->elemalg->copy(c->elemsize, cas->u.elemp, nil);
c->closed |= Rclosed;
incerr(c);
goto retc;
}
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;
@ -837,6 +830,13 @@ gotr:
ready(gp); ready(gp);
goto retc; goto retc;
rclose:
if(cas->u.elemp != nil)
c->elemalg->copy(c->elemsize, cas->u.elemp, nil);
c->closed |= Rclosed;
incerr(c);
goto retc;
gots: gots:
// send path to wakeup the receiver (sg) // send path to wakeup the receiver (sg)
if(debug) { if(debug) {
@ -848,14 +848,17 @@ gots:
sys·printint(o); sys·printint(o);
prints("\n"); prints("\n");
} }
if(c->closed & Wclosed) { if(c->closed & Wclosed)
incerr(c); goto sclose;
goto retc;
}
c->elemalg->copy(c->elemsize, sg->elem, cas->u.elem); c->elemalg->copy(c->elemsize, sg->elem, cas->u.elem);
gp = sg->g; gp = sg->g;
gp->param = sg; gp->param = sg;
ready(gp); ready(gp);
goto retc;
sclose:
incerr(c);
goto retc;
retc: retc:
if(sel->ncase >= 1 && sel->ncase < nelem(selfree)) { if(sel->ncase >= 1 && sel->ncase < nelem(selfree)) {
@ -909,7 +912,6 @@ sys·closechan(Hchan *c)
void void
sys·closedchan(Hchan *c, bool closed) sys·closedchan(Hchan *c, bool closed)
{ {
// test Rclosed // test Rclosed
closed = 0; closed = 0;
if(c->closed & Rclosed) if(c->closed & Rclosed)

197
test/closedchan.go Normal file
View file

@ -0,0 +1,197 @@
// $G $D/$F.go && $L $F.$A && ./$A.out
// Copyright 2009 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.
// Test close(c), closed(c).
//
// TODO(rsc): Doesn't check behavior of close(c) when there
// are blocked senders/receivers.
package main
type Chan interface {
Send(int);
Nbsend(int) bool;
Recv() int;
Nbrecv() (int, bool);
Close();
Closed() bool;
Impl() string;
}
// direct channel operations
type XChan chan int
func (c XChan) Send(x int) {
c <- x
}
func (c XChan) Nbsend(x int) bool {
return c <- x;
}
func (c XChan) Recv() int {
return <-c
}
func (c XChan) Nbrecv() (int, bool) {
x, ok := <-c;
return x, ok;
}
func (c XChan) Close() {
close(c)
}
func (c XChan) Closed() bool {
return closed(c)
}
func (c XChan) Impl() string {
return "(<- operator)"
}
// indirect operations via select
type SChan chan int
func (c SChan) Send(x int) {
select {
case c <- x:
}
}
func (c SChan) Nbsend(x int) bool {
select {
case c <- x:
return true;
default:
return false;
}
panic("nbsend");
}
func (c SChan) Recv() int {
select {
case x := <-c:
return x;
}
panic("recv");
}
func (c SChan) Nbrecv() (int, bool) {
select {
case x := <-c:
return x, true;
default:
return 0, false;
}
panic("nbrecv");
}
func (c SChan) Close() {
close(c)
}
func (c SChan) Closed() bool {
return closed(c)
}
func (c SChan) Impl() string {
return "(select)";
}
func test1(c Chan) {
// not closed until the close signal (a zero value) has been received.
if c.Closed() {
println("test1: Closed before Recv zero:", c.Impl());
}
for i := 0; i < 3; i++ {
// recv a close signal (a zero value)
if x := c.Recv(); x != 0 {
println("test1: recv on closed got non-zero:", x, c.Impl());
}
// should now be closed.
if !c.Closed() {
println("test1: not closed after recv zero", c.Impl());
}
// should work with ,ok: received a value without blocking, so ok == true.
x, ok := c.Nbrecv();
if !ok {
println("test1: recv on closed got not ok", c.Impl());
}
if x != 0 {
println("test1: recv ,ok on closed got non-zero:", x, c.Impl());
}
}
// send should work with ,ok too: sent a value without blocking, so ok == true.
ok := c.Nbsend(1);
if !ok {
println("test1: send on closed got not ok", c.Impl());
}
// but the value should have been discarded.
if x := c.Recv(); x != 0 {
println("test1: recv on closed got non-zero after send on closed:", x, c.Impl());
}
// similarly Send.
c.Send(2);
if x := c.Recv(); x != 0 {
println("test1: recv on closed got non-zero after send on closed:", x, c.Impl());
}
}
func testasync1(c Chan) {
// not closed until the close signal (a zero value) has been received.
if c.Closed() {
println("testasync1: Closed before Recv zero:", c.Impl());
}
// should be able to get the last value via Recv
if x := c.Recv(); x != 1 {
println("testasync1: Recv did not get 1:", x, c.Impl());
}
test1(c);
}
func testasync2(c Chan) {
// not closed until the close signal (a zero value) has been received.
if c.Closed() {
println("testasync2: Closed before Recv zero:", c.Impl());
}
// should be able to get the last value via Nbrecv
if x, ok := c.Nbrecv(); !ok || x != 1 {
println("testasync2: Nbrecv did not get 1, true:", x, ok, c.Impl());
}
test1(c);
}
func closedsync() chan int {
c := make(chan int);
close(c);
return c;
}
func closedasync() chan int {
c := make(chan int, 2);
c <- 1;
close(c);
return c;
}
func main() {
test1(XChan(closedsync()));
test1(SChan(closedsync()));
testasync1(XChan(closedasync()));
testasync1(SChan(closedasync()));
testasync2(XChan(closedasync()));
testasync2(SChan(closedasync()));
}