mirror of
https://github.com/golang/go
synced 2024-09-04 23:44:16 +00:00
![Josh Bleecher Snyder](/assets/img/avatar_default.png)
On a slow or distracted machine, 0.1s is sometimes not long enough for a non-blocking function call to complete. This causes rare test flakes. They can be easily reproduced by reducing the wait time to (say) 100ns. For non-blocking functions, increase the window from 100ms to 10s. Using different windows for block and non-blocking functions, allows us to reduce the time for blocking functions. The risk here is false negatives, but that risk is low; this test is run repeatedly on many fast machines, for which 10ms is ample time. This reduces the time required to run the test by a factor of 10, from ~1s to ~100ms. Fixes #20299 Change-Id: Ice9a641a66c6c101d738a2ebe1bcb144ae3c9916 Reviewed-on: https://go-review.googlesource.com/47812 Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
227 lines
4.1 KiB
Go
227 lines
4.1 KiB
Go
// 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:
|
|
}
|
|
})
|
|
}
|