1// runoutput
2
3// Copyright 2011 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// Generate test of channel operations and simple selects.
8// The output of this program is compiled and run to do the
9// actual test.
10
11// Each test does only one real send or receive at a time, but phrased
12// in various ways that the compiler may or may not rewrite
13// into simpler expressions.
14
15package main
16
17import (
18	"bufio"
19	"fmt"
20	"io"
21	"os"
22	"text/template"
23)
24
25func main() {
26	out := bufio.NewWriter(os.Stdout)
27	fmt.Fprintln(out, header)
28	a := new(arg)
29
30	// Generate each test as a separate function to avoid
31	// hitting the gc optimizer with one enormous function.
32	// If we name all the functions init we don't have to
33	// maintain a list of which ones to run.
34	do := func(t *template.Template) {
35		for ; next(); a.reset() {
36			fmt.Fprintln(out, `func init() {`)
37			run(t, a, out)
38			fmt.Fprintln(out, `}`)
39		}
40	}
41
42	do(recv)
43	do(send)
44	do(recvOrder)
45	do(sendOrder)
46	do(nonblock)
47
48	fmt.Fprintln(out, "//", a.nreset, "cases")
49	out.Flush()
50}
51
52func run(t *template.Template, a interface{}, out io.Writer) {
53	if err := t.Execute(out, a); err != nil {
54		panic(err)
55	}
56}
57
58type arg struct {
59	def    bool
60	nreset int
61}
62
63func (a *arg) Maybe() bool {
64	return maybe()
65}
66
67func (a *arg) MaybeDefault() bool {
68	if a.def {
69		return false
70	}
71	a.def = maybe()
72	return a.def
73}
74
75func (a *arg) MustDefault() bool {
76	return !a.def
77}
78
79func (a *arg) reset() {
80	a.def = false
81	a.nreset++
82}
83
84const header = `// GENERATED BY select5.go; DO NOT EDIT
85
86package main
87
88// channel is buffered so test is single-goroutine.
89// we are not interested in the concurrency aspects
90// of select, just testing that the right calls happen.
91var c = make(chan int, 1)
92var nilch chan int
93var n = 1
94var x int
95var i interface{}
96var dummy = make(chan int)
97var m = make(map[int]int)
98var order = 0
99
100func f(p *int) *int {
101	return p
102}
103
104// check order of operations by ensuring that
105// successive calls to checkorder have increasing o values.
106func checkorder(o int) {
107	if o <= order {
108		println("invalid order", o, "after", order)
109		panic("order")
110	}
111	order = o
112}
113
114func fc(c chan int, o int) chan int {
115	checkorder(o)
116	return c
117}
118
119func fp(p *int, o int) *int {
120	checkorder(o)
121	return p
122}
123
124func fn(n, o int) int {
125	checkorder(o)
126	return n
127}
128
129func die(x int) {
130	println("have", x, "want", n)
131	panic("chan")
132}
133
134func main() {
135	// everything happens in init funcs
136}
137`
138
139func parse(name, s string) *template.Template {
140	t, err := template.New(name).Parse(s)
141	if err != nil {
142		panic(fmt.Sprintf("%q: %s", name, err))
143	}
144	return t
145}
146
147var recv = parse("recv", `
148	{{/*  Send n, receive it one way or another into x, check that they match. */}}
149	c <- n
150	{{if .Maybe}}
151	x = <-c
152	{{else}}
153	select {
154	{{/*  Blocking or non-blocking, before the receive. */}}
155	{{/*  The compiler implements two-case select where one is default with custom code, */}}
156	{{/*  so test the default branch both before and after the send. */}}
157	{{if .MaybeDefault}}
158	default:
159		panic("nonblock")
160	{{end}}
161	{{/*  Receive from c.  Different cases are direct, indirect, :=, interface, and map assignment. */}}
162	{{if .Maybe}}
163	case x = <-c:
164	{{else}}{{if .Maybe}}
165	case *f(&x) = <-c:
166	{{else}}{{if .Maybe}}
167	case y := <-c:
168		x = y
169	{{else}}{{if .Maybe}}
170	case i = <-c:
171		x = i.(int)
172	{{else}}
173	case m[13] = <-c:
174		x = m[13]
175	{{end}}{{end}}{{end}}{{end}}
176	{{/*  Blocking or non-blocking again, after the receive. */}}
177	{{if .MaybeDefault}}
178	default:
179		panic("nonblock")
180	{{end}}
181	{{/*  Dummy send, receive to keep compiler from optimizing select. */}}
182	{{if .Maybe}}
183	case dummy <- 1:
184		panic("dummy send")
185	{{end}}
186	{{if .Maybe}}
187	case <-dummy:
188		panic("dummy receive")
189	{{end}}
190	{{/*  Nil channel send, receive to keep compiler from optimizing select. */}}
191	{{if .Maybe}}
192	case nilch <- 1:
193		panic("nilch send")
194	{{end}}
195	{{if .Maybe}}
196	case <-nilch:
197		panic("nilch recv")
198	{{end}}
199	}
200	{{end}}
201	if x != n {
202		die(x)
203	}
204	n++
205`)
206
207var recvOrder = parse("recvOrder", `
208	{{/*  Send n, receive it one way or another into x, check that they match. */}}
209	{{/*  Check order of operations along the way by calling functions that check */}}
210	{{/*  that the argument sequence is strictly increasing. */}}
211	order = 0
212	c <- n
213	{{if .Maybe}}
214	{{/*  Outside of select, left-to-right rule applies. */}}
215	{{/*  (Inside select, assignment waits until case is chosen, */}}
216	{{/*  so right hand side happens before anything on left hand side. */}}
217	*fp(&x, 1) = <-fc(c, 2)
218	{{else}}{{if .Maybe}}
219	m[fn(13, 1)] = <-fc(c, 2)
220	x = m[13]
221	{{else}}
222	select {
223	{{/*  Blocking or non-blocking, before the receive. */}}
224	{{/*  The compiler implements two-case select where one is default with custom code, */}}
225	{{/*  so test the default branch both before and after the send. */}}
226	{{if .MaybeDefault}}
227	default:
228		panic("nonblock")
229	{{end}}
230	{{/*  Receive from c.  Different cases are direct, indirect, :=, interface, and map assignment. */}}
231	{{if .Maybe}}
232	case *fp(&x, 100) = <-fc(c, 1):
233	{{else}}{{if .Maybe}}
234	case y := <-fc(c, 1):
235		x = y
236	{{else}}{{if .Maybe}}
237	case i = <-fc(c, 1):
238		x = i.(int)
239	{{else}}
240	case m[fn(13, 100)] = <-fc(c, 1):
241		x = m[13]
242	{{end}}{{end}}{{end}}
243	{{/*  Blocking or non-blocking again, after the receive. */}}
244	{{if .MaybeDefault}}
245	default:
246		panic("nonblock")
247	{{end}}
248	{{/*  Dummy send, receive to keep compiler from optimizing select. */}}
249	{{if .Maybe}}
250	case fc(dummy, 2) <- fn(1, 3):
251		panic("dummy send")
252	{{end}}
253	{{if .Maybe}}
254	case <-fc(dummy, 4):
255		panic("dummy receive")
256	{{end}}
257	{{/*  Nil channel send, receive to keep compiler from optimizing select. */}}
258	{{if .Maybe}}
259	case fc(nilch, 5) <- fn(1, 6):
260		panic("nilch send")
261	{{end}}
262	{{if .Maybe}}
263	case <-fc(nilch, 7):
264		panic("nilch recv")
265	{{end}}
266	}
267	{{end}}{{end}}
268	if x != n {
269		die(x)
270	}
271	n++
272`)
273
274var send = parse("send", `
275	{{/*  Send n one way or another, receive it into x, check that they match. */}}
276	{{if .Maybe}}
277	c <- n
278	{{else}}
279	select {
280	{{/*  Blocking or non-blocking, before the receive (same reason as in recv). */}}
281	{{if .MaybeDefault}}
282	default:
283		panic("nonblock")
284	{{end}}
285	{{/*  Send c <- n.  No real special cases here, because no values come back */}}
286	{{/*  from the send operation. */}}
287	case c <- n:
288	{{/*  Blocking or non-blocking. */}}
289	{{if .MaybeDefault}}
290	default:
291		panic("nonblock")
292	{{end}}
293	{{/*  Dummy send, receive to keep compiler from optimizing select. */}}
294	{{if .Maybe}}
295	case dummy <- 1:
296		panic("dummy send")
297	{{end}}
298	{{if .Maybe}}
299	case <-dummy:
300		panic("dummy receive")
301	{{end}}
302	{{/*  Nil channel send, receive to keep compiler from optimizing select. */}}
303	{{if .Maybe}}
304	case nilch <- 1:
305		panic("nilch send")
306	{{end}}
307	{{if .Maybe}}
308	case <-nilch:
309		panic("nilch recv")
310	{{end}}
311	}
312	{{end}}
313	x = <-c
314	if x != n {
315		die(x)
316	}
317	n++
318`)
319
320var sendOrder = parse("sendOrder", `
321	{{/*  Send n one way or another, receive it into x, check that they match. */}}
322	{{/*  Check order of operations along the way by calling functions that check */}}
323	{{/*  that the argument sequence is strictly increasing. */}}
324	order = 0
325	{{if .Maybe}}
326	fc(c, 1) <- fn(n, 2)
327	{{else}}
328	select {
329	{{/*  Blocking or non-blocking, before the receive (same reason as in recv). */}}
330	{{if .MaybeDefault}}
331	default:
332		panic("nonblock")
333	{{end}}
334	{{/*  Send c <- n.  No real special cases here, because no values come back */}}
335	{{/*  from the send operation. */}}
336	case fc(c, 1) <- fn(n, 2):
337	{{/*  Blocking or non-blocking. */}}
338	{{if .MaybeDefault}}
339	default:
340		panic("nonblock")
341	{{end}}
342	{{/*  Dummy send, receive to keep compiler from optimizing select. */}}
343	{{if .Maybe}}
344	case fc(dummy, 3) <- fn(1, 4):
345		panic("dummy send")
346	{{end}}
347	{{if .Maybe}}
348	case <-fc(dummy, 5):
349		panic("dummy receive")
350	{{end}}
351	{{/*  Nil channel send, receive to keep compiler from optimizing select. */}}
352	{{if .Maybe}}
353	case fc(nilch, 6) <- fn(1, 7):
354		panic("nilch send")
355	{{end}}
356	{{if .Maybe}}
357	case <-fc(nilch, 8):
358		panic("nilch recv")
359	{{end}}
360	}
361	{{end}}
362	x = <-c
363	if x != n {
364		die(x)
365	}
366	n++
367`)
368
369var nonblock = parse("nonblock", `
370	x = n
371	{{/*  Test various combinations of non-blocking operations. */}}
372	{{/*  Receive assignments must not edit or even attempt to compute the address of the lhs. */}}
373	select {
374	{{if .MaybeDefault}}
375	default:
376	{{end}}
377	{{if .Maybe}}
378	case dummy <- 1:
379		panic("dummy <- 1")
380	{{end}}
381	{{if .Maybe}}
382	case nilch <- 1:
383		panic("nilch <- 1")
384	{{end}}
385	{{if .Maybe}}
386	case <-dummy:
387		panic("<-dummy")
388	{{end}}
389	{{if .Maybe}}
390	case x = <-dummy:
391		panic("<-dummy x")
392	{{end}}
393	{{if .Maybe}}
394	case **(**int)(nil) = <-dummy:
395		panic("<-dummy (and didn't crash saving result!)")
396	{{end}}
397	{{if .Maybe}}
398	case <-nilch:
399		panic("<-nilch")
400	{{end}}
401	{{if .Maybe}}
402	case x = <-nilch:
403		panic("<-nilch x")
404	{{end}}
405	{{if .Maybe}}
406	case **(**int)(nil) = <-nilch:
407		panic("<-nilch (and didn't crash saving result!)")
408	{{end}}
409	{{if .MustDefault}}
410	default:
411	{{end}}
412	}
413	if x != n {
414		die(x)
415	}
416	n++
417`)
418
419// Code for enumerating all possible paths through
420// some logic.  The logic should call choose(n) when
421// it wants to choose between n possibilities.
422// On successive runs through the logic, choose(n)
423// will return 0, 1, ..., n-1.  The helper maybe() is
424// similar but returns true and then false.
425//
426// Given a function gen that generates an output
427// using choose and maybe, code can generate all
428// possible outputs using
429//
430//	for next() {
431//		gen()
432//	}
433
434type choice struct {
435	i, n int
436}
437
438var choices []choice
439var cp int = -1
440
441func maybe() bool {
442	return choose(2) == 0
443}
444
445func choose(n int) int {
446	if cp >= len(choices) {
447		// never asked this before: start with 0.
448		choices = append(choices, choice{0, n})
449		cp = len(choices)
450		return 0
451	}
452	// otherwise give recorded answer
453	if n != choices[cp].n {
454		panic("inconsistent choices")
455	}
456	i := choices[cp].i
457	cp++
458	return i
459}
460
461func next() bool {
462	if cp < 0 {
463		// start a new round
464		cp = 0
465		return true
466	}
467
468	// increment last choice sequence
469	cp = len(choices) - 1
470	for cp >= 0 && choices[cp].i == choices[cp].n-1 {
471		cp--
472	}
473	if cp < 0 {
474		choices = choices[:0]
475		return false
476	}
477	choices[cp].i++
478	choices = choices[:cp+1]
479	cp = 0
480	return true
481}
482