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
10package main
11
12import "time"
13
14const always = "function did not"
15const never = "function did"
16
17func unreachable() {
18	panic("control flow shouldn't reach here")
19}
20
21// Calls f and verifies that f always/never panics depending on signal.
22func 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.
36func 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
58func 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