| // 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: | |
| } | |
| }) | |
| } |