aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/chan_test.go
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2015-03-13 13:37:57 +0300
committerDmitry Vyukov <dvyukov@google.com>2015-03-18 08:57:30 +0000
commitfcb895feef1c9214f9cb633b5a0fa4dc8f46af9e (patch)
tree954038131b8667899c76898a130ed8489c556381 /src/runtime/chan_test.go
parentecd630de6dd2e5285f18c650207b5c84649402db (diff)
downloadgo-fcb895feef1c9214f9cb633b5a0fa4dc8f46af9e.tar.gz
go-fcb895feef1c9214f9cb633b5a0fa4dc8f46af9e.zip
runtime: add a select test
One of my earlier versions of finer-grained select locking failed on this test. If you just naively lock and check channels one-by-one, it is possible that you skip over ready channels. Consider that initially c1 is ready and c2 is not. Select checks c2. Then another goroutine makes c1 not ready and c2 ready (in that order). Then select checks c1, concludes that no channels are ready and executes the default case. But there was no point in time when no channel is ready and so default case must not be executed. Change-Id: I3594bf1f36cfb120be65e2474794f0562aebcbbd Reviewed-on: https://go-review.googlesource.com/7550 Reviewed-by: Russ Cox <rsc@golang.org>
Diffstat (limited to 'src/runtime/chan_test.go')
-rw-r--r--src/runtime/chan_test.go75
1 files changed, 75 insertions, 0 deletions
diff --git a/src/runtime/chan_test.go b/src/runtime/chan_test.go
index 66dfd6f8d8..0b918bb99f 100644
--- a/src/runtime/chan_test.go
+++ b/src/runtime/chan_test.go
@@ -218,6 +218,81 @@ func TestNonblockRecvRace(t *testing.T) {
}
}
+// This test checks that select acts on the state of the channels at one
+// moment in the execution, not over a smeared time window.
+// In the test, one goroutine does:
+// create c1, c2
+// make c1 ready for receiving
+// create second goroutine
+// make c2 ready for receiving
+// make c1 no longer ready for receiving (if possible)
+// The second goroutine does a non-blocking select receiving from c1 and c2.
+// From the time the second goroutine is created, at least one of c1 and c2
+// is always ready for receiving, so the select in the second goroutine must
+// always receive from one or the other. It must never execute the default case.
+func TestNonblockSelectRace(t *testing.T) {
+ n := 100000
+ if testing.Short() {
+ n = 1000
+ }
+ done := make(chan bool, 1)
+ for i := 0; i < n; i++ {
+ c1 := make(chan int, 1)
+ c2 := make(chan int, 1)
+ c1 <- 1
+ go func() {
+ select {
+ case <-c1:
+ case <-c2:
+ default:
+ done <- false
+ return
+ }
+ done <- true
+ }()
+ c2 <- 1
+ select {
+ case <-c1:
+ default:
+ }
+ if !<-done {
+ t.Fatal("no chan is ready")
+ }
+ }
+}
+
+// Same as TestNonblockSelectRace, but close(c2) replaces c2 <- 1.
+func TestNonblockSelectRace2(t *testing.T) {
+ n := 100000
+ if testing.Short() {
+ n = 1000
+ }
+ done := make(chan bool, 1)
+ for i := 0; i < n; i++ {
+ c1 := make(chan int, 1)
+ c2 := make(chan int)
+ c1 <- 1
+ go func() {
+ select {
+ case <-c1:
+ case <-c2:
+ default:
+ done <- false
+ return
+ }
+ done <- true
+ }()
+ close(c2)
+ select {
+ case <-c1:
+ default:
+ }
+ if !<-done {
+ t.Fatal("no chan is ready")
+ }
+ }
+}
+
func TestSelfSelect(t *testing.T) {
// Ensure that send/recv on the same chan in select
// does not crash nor deadlock.