]> Cypherpunks.ru repositories - gostls13.git/commitdiff
add test for close/closed, fix a few implementation bugs.
authorRuss Cox <rsc@golang.org>
Tue, 24 Mar 2009 01:50:35 +0000 (18:50 -0700)
committerRuss Cox <rsc@golang.org>
Tue, 24 Mar 2009 01:50:35 +0000 (18:50 -0700)
R=ken
OCL=26664
CL=26664

src/runtime/chan.c
test/closedchan.go [new file with mode: 0644]

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