diff options
Diffstat (limited to '')
-rw-r--r-- | test/closedchan.go | 359 |
1 files changed, 359 insertions, 0 deletions
diff --git a/test/closedchan.go b/test/closedchan.go new file mode 100644 index 0000000..043a92d --- /dev/null +++ b/test/closedchan.go @@ -0,0 +1,359 @@ +// run + +// 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), receive of closed channel. +// +// TODO(rsc): Doesn't check behavior of close(c) when there +// are blocked senders/receivers. + +package main + +import "os" + +var failed bool + +type Chan interface { + Send(int) + Nbsend(int) bool + Recv() (int) + Nbrecv() (int, bool) + Recv2() (int, bool) + Nbrecv2() (int, bool, bool) + Close() + Impl() string +} + +// direct channel operations when possible +type XChan chan int + +func (c XChan) Send(x int) { + c <- x +} + +func (c XChan) Nbsend(x int) bool { + select { + case c <- x: + return true + default: + return false + } + panic("nbsend") +} + +func (c XChan) Recv() int { + return <-c +} + +func (c XChan) Nbrecv() (int, bool) { + select { + case x := <-c: + return x, true + default: + return 0, false + } + panic("nbrecv") +} + +func (c XChan) Recv2() (int, bool) { + x, ok := <-c + return x, ok +} + +func (c XChan) Nbrecv2() (int, bool, bool) { + select { + case x, ok := <-c: + return x, ok, true + default: + return 0, false, false + } + panic("nbrecv2") +} + +func (c XChan) Close() { + close(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 { + default: + return false + case c <- x: + return true + } + panic("nbsend") +} + +func (c SChan) Recv() int { + select { + case x := <-c: + return x + } + panic("recv") +} + +func (c SChan) Nbrecv() (int, bool) { + select { + default: + return 0, false + case x := <-c: + return x, true + } + panic("nbrecv") +} + +func (c SChan) Recv2() (int, bool) { + select { + case x, ok := <-c: + return x, ok + } + panic("recv") +} + +func (c SChan) Nbrecv2() (int, bool, bool) { + select { + default: + return 0, false, false + case x, ok := <-c: + return x, ok, true + } + panic("nbrecv") +} + +func (c SChan) Close() { + close(c) +} + +func (c SChan) Impl() string { + return "(select)" +} + +// indirect operations via larger selects +var dummy = make(chan bool) + +type SSChan chan int + +func (c SSChan) Send(x int) { + select { + case c <- x: + case <-dummy: + } +} + +func (c SSChan) Nbsend(x int) bool { + select { + default: + return false + case <-dummy: + case c <- x: + return true + } + panic("nbsend") +} + +func (c SSChan) Recv() int { + select { + case <-dummy: + case x := <-c: + return x + } + panic("recv") +} + +func (c SSChan) Nbrecv() (int, bool) { + select { + case <-dummy: + default: + return 0, false + case x := <-c: + return x, true + } + panic("nbrecv") +} + +func (c SSChan) Recv2() (int, bool) { + select { + case <-dummy: + case x, ok := <-c: + return x, ok + } + panic("recv") +} + +func (c SSChan) Nbrecv2() (int, bool, bool) { + select { + case <-dummy: + default: + return 0, false, false + case x, ok := <-c: + return x, ok, true + } + panic("nbrecv") +} + +func (c SSChan) Close() { + close(c) +} + +func (c SSChan) Impl() string { + return "(select)" +} + + +func shouldPanic(f func()) { + defer func() { + if recover() == nil { + panic("did not panic") + } + }() + f() +} + +func test1(c Chan) { + for i := 0; i < 3; i++ { + // recv a close signal (a zero value) + if x := c.Recv(); x != 0 { + println("test1: recv on closed:", x, c.Impl()) + failed = true + } + if x, ok := c.Recv2(); x != 0 || ok { + println("test1: recv2 on closed:", x, ok, c.Impl()) + failed = true + } + + // should work with select: received a value without blocking, so selected == true. + x, selected := c.Nbrecv() + if x != 0 || !selected { + println("test1: recv on closed nb:", x, selected, c.Impl()) + failed = true + } + x, ok, selected := c.Nbrecv2() + if x != 0 || ok || !selected { + println("test1: recv2 on closed nb:", x, ok, selected, c.Impl()) + failed = true + } + } + + // send should work with ,ok too: sent a value without blocking, so ok == true. + shouldPanic(func() { c.Nbsend(1) }) + + // 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()) + failed = true + } + + // similarly Send. + shouldPanic(func() { c.Send(2) }) + if x := c.Recv(); x != 0 { + println("test1: recv on closed got non-zero after send on closed:", x, c.Impl()) + failed = true + } +} + +func testasync1(c Chan) { + // 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()) + failed = true + } + + test1(c) +} + +func testasync2(c Chan) { + // should be able to get the last value via Recv2 + if x, ok := c.Recv2(); x != 1 || !ok { + println("testasync1: Recv did not get 1, true:", x, ok, c.Impl()) + failed = true + } + + test1(c) +} + +func testasync3(c Chan) { + // should be able to get the last value via Nbrecv + if x, selected := c.Nbrecv(); x != 1 || !selected { + println("testasync2: Nbrecv did not get 1, true:", x, selected, c.Impl()) + failed = true + } + + test1(c) +} + +func testasync4(c Chan) { + // should be able to get the last value via Nbrecv2 + if x, ok, selected := c.Nbrecv2(); x != 1 || !ok || !selected { + println("testasync2: Nbrecv did not get 1, true, true:", x, ok, selected, c.Impl()) + failed = true + } + 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 +} + +var mks = []func(chan int) Chan { + func(c chan int) Chan { return XChan(c) }, + func(c chan int) Chan { return SChan(c) }, + func(c chan int) Chan { return SSChan(c) }, +} + +var testcloseds = []func(Chan) { + testasync1, + testasync2, + testasync3, + testasync4, +} + +func main() { + for _, mk := range mks { + test1(mk(closedsync())) + } + + for _, testclosed := range testcloseds { + for _, mk := range mks { + testclosed(mk(closedasync())) + } + } + + var ch chan int + shouldPanic(func() { + close(ch) + }) + + ch = make(chan int) + close(ch) + shouldPanic(func() { + close(ch) + }) + + if failed { + os.Exit(1) + } +} |