From 13584f4a23f2b6a431c3733f8d3469702890d7a9 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Mon, 23 Mar 2009 18:50:35 -0700 Subject: [PATCH] add test for close/closed, fix a few implementation bugs. R=ken OCL=26664 CL=26664 --- src/runtime/chan.c | 58 ++++++------- test/closedchan.go | 197 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 227 insertions(+), 28 deletions(-) create mode 100644 test/closedchan.go diff --git a/src/runtime/chan.c b/src/runtime/chan.c index c5e53410e8..59ac78d79c 100644 --- a/src/runtime/chan.c +++ b/src/runtime/chan.c @@ -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 index 0000000000..4ab12c7756 --- /dev/null +++ b/test/closedchan.go @@ -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())); +}