blob: dd14c7381eae70c6974cf837b93028ca5d123363 [file] [log] [blame]
// run
// Copyright 2010 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 the semantics of the select statement
// for basic empty/non-empty cases.
package main
import "time"
const always = "function did not"
const never = "function did"
func unreachable() {
panic("control flow shouldn't reach here")
}
// Calls f and verifies that f always/never panics depending on signal.
func testPanic(signal string, f func()) {
defer func() {
s := never
if recover() != nil {
s = always // f panicked
}
if s != signal {
panic(signal + " panic")
}
}()
f()
}
// Calls f and empirically verifies that f always/never blocks depending on signal.
func testBlock(signal string, f func()) {
c := make(chan string)
go func() {
f()
c <- never // f didn't block
}()
go func() {
if signal == never {
// Wait a long time to make sure that we don't miss our window by accident on a slow machine.
time.Sleep(10 * time.Second)
} else {
// Wait as short a time as we can without false negatives.
// 10ms should be long enough to catch most failures.
time.Sleep(10 * time.Millisecond)
}
c <- always // f blocked always
}()
if <-c != signal {
panic(signal + " block")
}
}
func main() {
const async = 1 // asynchronous channels
var nilch chan int
closedch := make(chan int)
close(closedch)
// sending/receiving from a nil channel blocks
testBlock(always, func() {
nilch <- 7
})
testBlock(always, func() {
<-nilch
})
// sending/receiving from a nil channel inside a select is never selected
testPanic(never, func() {
select {
case nilch <- 7:
unreachable()
default:
}
})
testPanic(never, func() {
select {
case <-nilch:
unreachable()
default:
}
})
// sending to an async channel with free buffer space never blocks
testBlock(never, func() {
ch := make(chan int, async)
ch <- 7
})
// receiving from a closed channel never blocks
testBlock(never, func() {
for i := 0; i < 10; i++ {
if <-closedch != 0 {
panic("expected zero value when reading from closed channel")
}
if x, ok := <-closedch; x != 0 || ok {
println("closedch:", x, ok)
panic("expected 0, false from closed channel")
}
}
})
// sending to a closed channel panics.
testPanic(always, func() {
closedch <- 7
})
// receiving from a non-ready channel always blocks
testBlock(always, func() {
ch := make(chan int)
<-ch
})
// empty selects always block
testBlock(always, func() {
select {}
})
// selects with only nil channels always block
testBlock(always, func() {
select {
case <-nilch:
unreachable()
}
})
testBlock(always, func() {
select {
case nilch <- 7:
unreachable()
}
})
testBlock(always, func() {
select {
case <-nilch:
unreachable()
case nilch <- 7:
unreachable()
}
})
// selects with non-ready non-nil channels always block
testBlock(always, func() {
ch := make(chan int)
select {
case <-ch:
unreachable()
}
})
// selects with default cases don't block
testBlock(never, func() {
select {
default:
}
})
testBlock(never, func() {
select {
case <-nilch:
unreachable()
default:
}
})
testBlock(never, func() {
select {
case nilch <- 7:
unreachable()
default:
}
})
// selects with ready channels don't block
testBlock(never, func() {
ch := make(chan int, async)
select {
case ch <- 7:
default:
unreachable()
}
})
testBlock(never, func() {
ch := make(chan int, async)
ch <- 7
select {
case <-ch:
default:
unreachable()
}
})
// selects with closed channels behave like ordinary operations
testBlock(never, func() {
select {
case <-closedch:
}
})
testBlock(never, func() {
select {
case x := (<-closedch):
_ = x
}
})
testBlock(never, func() {
select {
case x, ok := (<-closedch):
_, _ = x, ok
}
})
testPanic(always, func() {
select {
case closedch <- 7:
}
})
// select should not get confused if it sees itself
testBlock(always, func() {
c := make(chan int)
select {
case c <- 1:
case <-c:
}
})
}