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