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
17
18func 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.
24func 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.
39func 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
55func 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