Home | History | Annotate | Download | only in chan
      1 // run
      2 
      3 // Copyright 2010 The Go Authors. All rights reserved.
      4 // Use of this source code is governed by a BSD-style
      5 // license that can be found in the LICENSE file.
      6 
      7 // Test the semantics of the select statement
      8 // for basic empty/non-empty cases.
      9 
     10 package main
     11 
     12 import "time"
     13 
     14 const always = "function did not"
     15 const never = "function did"
     16 
     17 func unreachable() {
     18 	panic("control flow shouldn't reach here")
     19 }
     20 
     21 // Calls f and verifies that f always/never panics depending on signal.
     22 func testPanic(signal string, f func()) {
     23 	defer func() {
     24 		s := never
     25 		if recover() != nil {
     26 			s = always // f panicked
     27 		}
     28 		if s != signal {
     29 			panic(signal + " panic")
     30 		}
     31 	}()
     32 	f()
     33 }
     34 
     35 // Calls f and empirically verifies that f always/never blocks depending on signal.
     36 func testBlock(signal string, f func()) {
     37 	c := make(chan string)
     38 	go func() {
     39 		f()
     40 		c <- never // f didn't block
     41 	}()
     42 	go func() {
     43 		if signal == never {
     44 			// Wait a long time to make sure that we don't miss our window by accident on a slow machine.
     45 			time.Sleep(10 * time.Second)
     46 		} else {
     47 			// Wait as short a time as we can without false negatives.
     48 			// 10ms should be long enough to catch most failures.
     49 			time.Sleep(10 * time.Millisecond)
     50 		}
     51 		c <- always // f blocked always
     52 	}()
     53 	if <-c != signal {
     54 		panic(signal + " block")
     55 	}
     56 }
     57 
     58 func main() {
     59 	const async = 1 // asynchronous channels
     60 	var nilch chan int
     61 	closedch := make(chan int)
     62 	close(closedch)
     63 
     64 	// sending/receiving from a nil channel blocks
     65 	testBlock(always, func() {
     66 		nilch <- 7
     67 	})
     68 	testBlock(always, func() {
     69 		<-nilch
     70 	})
     71 
     72 	// sending/receiving from a nil channel inside a select is never selected
     73 	testPanic(never, func() {
     74 		select {
     75 		case nilch <- 7:
     76 			unreachable()
     77 		default:
     78 		}
     79 	})
     80 	testPanic(never, func() {
     81 		select {
     82 		case <-nilch:
     83 			unreachable()
     84 		default:
     85 		}
     86 	})
     87 
     88 	// sending to an async channel with free buffer space never blocks
     89 	testBlock(never, func() {
     90 		ch := make(chan int, async)
     91 		ch <- 7
     92 	})
     93 
     94 	// receiving from a closed channel never blocks
     95 	testBlock(never, func() {
     96 		for i := 0; i < 10; i++ {
     97 			if <-closedch != 0 {
     98 				panic("expected zero value when reading from closed channel")
     99 			}
    100 			if x, ok := <-closedch; x != 0 || ok {
    101 				println("closedch:", x, ok)
    102 				panic("expected 0, false from closed channel")
    103 			}
    104 		}
    105 	})
    106 
    107 	// sending to a closed channel panics.
    108 	testPanic(always, func() {
    109 		closedch <- 7
    110 	})
    111 
    112 	// receiving from a non-ready channel always blocks
    113 	testBlock(always, func() {
    114 		ch := make(chan int)
    115 		<-ch
    116 	})
    117 
    118 	// empty selects always block
    119 	testBlock(always, func() {
    120 		select {}
    121 	})
    122 
    123 	// selects with only nil channels always block
    124 	testBlock(always, func() {
    125 		select {
    126 		case <-nilch:
    127 			unreachable()
    128 		}
    129 	})
    130 	testBlock(always, func() {
    131 		select {
    132 		case nilch <- 7:
    133 			unreachable()
    134 		}
    135 	})
    136 	testBlock(always, func() {
    137 		select {
    138 		case <-nilch:
    139 			unreachable()
    140 		case nilch <- 7:
    141 			unreachable()
    142 		}
    143 	})
    144 
    145 	// selects with non-ready non-nil channels always block
    146 	testBlock(always, func() {
    147 		ch := make(chan int)
    148 		select {
    149 		case <-ch:
    150 			unreachable()
    151 		}
    152 	})
    153 
    154 	// selects with default cases don't block
    155 	testBlock(never, func() {
    156 		select {
    157 		default:
    158 		}
    159 	})
    160 	testBlock(never, func() {
    161 		select {
    162 		case <-nilch:
    163 			unreachable()
    164 		default:
    165 		}
    166 	})
    167 	testBlock(never, func() {
    168 		select {
    169 		case nilch <- 7:
    170 			unreachable()
    171 		default:
    172 		}
    173 	})
    174 
    175 	// selects with ready channels don't block
    176 	testBlock(never, func() {
    177 		ch := make(chan int, async)
    178 		select {
    179 		case ch <- 7:
    180 		default:
    181 			unreachable()
    182 		}
    183 	})
    184 	testBlock(never, func() {
    185 		ch := make(chan int, async)
    186 		ch <- 7
    187 		select {
    188 		case <-ch:
    189 		default:
    190 			unreachable()
    191 		}
    192 	})
    193 
    194 	// selects with closed channels behave like ordinary operations
    195 	testBlock(never, func() {
    196 		select {
    197 		case <-closedch:
    198 		}
    199 	})
    200 	testBlock(never, func() {
    201 		select {
    202 		case x := (<-closedch):
    203 			_ = x
    204 		}
    205 	})
    206 	testBlock(never, func() {
    207 		select {
    208 		case x, ok := (<-closedch):
    209 			_, _ = x, ok
    210 		}
    211 	})
    212 	testPanic(always, func() {
    213 		select {
    214 		case closedch <- 7:
    215 		}
    216 	})
    217 
    218 	// select should not get confused if it sees itself
    219 	testBlock(always, func() {
    220 		c := make(chan int)
    221 		select {
    222 		case c <- 1:
    223 		case <-c:
    224 		}
    225 	})
    226 }
    227