Commit fcb895fe authored by Dmitry Vyukov's avatar Dmitry Vyukov

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/7550Reviewed-by: default avatarRuss Cox <rsc@golang.org>
parent ecd630de
......@@ -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.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment