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 
     18 func unreachable() {
     19 	panic("control flow shouldn't reach here")
     20 }
     21 
     22 
     23 // Calls f and verifies that f always/never panics depending on signal.
     24 func testPanic(signal string, f func()) {
     25 	defer func() {
     26 		s := never
     27 		if recover() != nil {
     28 			s = always // f panicked
     29 		}
     30 		if s != signal {
     31 			panic(signal + " panic")
     32 		}
     33 	}()
     34 	f()
     35 }
     36 
     37 
     38 // Calls f and empirically verifies that f always/never blocks depending on signal.
     39 func testBlock(signal string, f func()) {
     40 	c := make(chan string)
     41 	go func() {
     42 		f()
     43 		c <- never // f didn't block
     44 	}()
     45 	go func() {
     46 		time.Sleep(1e8) // 0.1s seems plenty long
     47 		c <- always     // f blocked always
     48 	}()
     49 	if <-c != signal {
     50 		panic(signal + " block")
     51 	}
     52 }
     53 
     54 
     55 func main() {
     56 	const async = 1 // asynchronous channels
     57 	var nilch chan int
     58 	closedch := make(chan int)
     59 	close(closedch)
     60 
     61 	// sending/receiving from a nil channel blocks
     62 	testBlock(always, func() {
     63 		nilch <- 7
     64 	})
     65 	testBlock(always, func() {
     66 		<-nilch
     67 	})
     68 
     69 	// sending/receiving from a nil channel inside a select is never selected
     70 	testPanic(never, func() {
     71 		select {
     72 		case nilch <- 7:
     73 			unreachable()
     74 		default:
     75 		}
     76 	})
     77 	testPanic(never, func() {
     78 		select {
     79 		case <-nilch:
     80 			unreachable()
     81 		default:
     82 		}
     83 	})
     84 
     85 	// sending to an async channel with free buffer space never blocks
     86 	testBlock(never, func() {
     87 		ch := make(chan int, async)
     88 		ch <- 7
     89 	})
     90 
     91 	// receiving from a closed channel never blocks
     92 	testBlock(never, func() {
     93 		for i := 0; i < 10; i++ {
     94 			if <-closedch != 0 {
     95 				panic("expected zero value when reading from closed channel")
     96 			}
     97 			if x, ok := <-closedch; x != 0 || ok {
     98 				println("closedch:", x, ok)
     99 				panic("expected 0, false from closed channel")
    100 			}
    101 		}
    102 	})
    103 
    104 	// sending to a closed channel panics.
    105 	testPanic(always, func() {
    106 		closedch <- 7
    107 	})
    108 
    109 	// receiving from a non-ready channel always blocks
    110 	testBlock(always, func() {
    111 		ch := make(chan int)
    112 		<-ch
    113 	})
    114 
    115 	// empty selects always block
    116 	testBlock(always, func() {
    117 		select {
    118 		}
    119 	})
    120 
    121 	// selects with only nil channels always block
    122 	testBlock(always, func() {
    123 		select {
    124 		case <-nilch:
    125 			unreachable()
    126 		}
    127 	})
    128 	testBlock(always, func() {
    129 		select {
    130 		case nilch <- 7:
    131 			unreachable()
    132 		}
    133 	})
    134 	testBlock(always, func() {
    135 		select {
    136 		case <-nilch:
    137 			unreachable()
    138 		case nilch <- 7:
    139 			unreachable()
    140 		}
    141 	})
    142 
    143 	// selects with non-ready non-nil channels always block
    144 	testBlock(always, func() {
    145 		ch := make(chan int)
    146 		select {
    147 		case <-ch:
    148 			unreachable()
    149 		}
    150 	})
    151 
    152 	// selects with default cases don't block
    153 	testBlock(never, func() {
    154 		select {
    155 		default:
    156 		}
    157 	})
    158 	testBlock(never, func() {
    159 		select {
    160 		case <-nilch:
    161 			unreachable()
    162 		default:
    163 		}
    164 	})
    165 	testBlock(never, func() {
    166 		select {
    167 		case nilch <- 7:
    168 			unreachable()
    169 		default:
    170 		}
    171 	})
    172 
    173 	// selects with ready channels don't block
    174 	testBlock(never, func() {
    175 		ch := make(chan int, async)
    176 		select {
    177 		case ch <- 7:
    178 		default:
    179 			unreachable()
    180 		}
    181 	})
    182 	testBlock(never, func() {
    183 		ch := make(chan int, async)
    184 		ch <- 7
    185 		select {
    186 		case <-ch:
    187 		default:
    188 			unreachable()
    189 		}
    190 	})
    191 
    192 	// selects with closed channels behave like ordinary operations
    193 	testBlock(never, func() {
    194 		select {
    195 		case <-closedch:
    196 		}
    197 	})
    198 	testBlock(never, func() {
    199 		select {
    200 		case x := (<-closedch):
    201 			_ = x
    202 		}
    203 	})
    204 	testBlock(never, func() {
    205 		select {
    206 		case x, ok := (<-closedch):
    207 			_, _ = x, ok
    208 		}
    209 	})
    210 	testPanic(always, func() {
    211 		select {
    212 		case closedch <- 7:
    213 		}
    214 	})
    215 
    216 	// select should not get confused if it sees itself
    217 	testBlock(always, func() {
    218 		c := make(chan int)
    219 		select {
    220 		case c <- 1:
    221 		case <-c:
    222 		}
    223 	})
    224 }
    225