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