mirror of
https://github.com/golang/go
synced 2024-11-22 10:34:46 -07:00
3fb5f329b9
R=golang-dev, bradfitz CC=golang-dev https://golang.org/cl/5677094
484 lines
10 KiB
Go
484 lines
10 KiB
Go
// $G $D/$F.go && $L $F.$A && ./$A.out >tmp.go &&
|
|
// $G tmp.go && $L tmp.$A && ./$A.out || echo BUG: select5
|
|
// rm -f tmp.go
|
|
|
|
// Copyright 2011 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Generate test of channel operations and simple selects.
|
|
// The output of this program is compiled and run to do the
|
|
// actual test.
|
|
|
|
// Each test does only one real send or receive at a time, but phrased
|
|
// in various ways that the compiler may or may not rewrite
|
|
// into simpler expressions.
|
|
|
|
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"text/template"
|
|
)
|
|
|
|
func main() {
|
|
out := bufio.NewWriter(os.Stdout)
|
|
fmt.Fprintln(out, header)
|
|
a := new(arg)
|
|
|
|
// Generate each kind of test as a separate function to avoid
|
|
// hitting the 6g optimizer with one enormous function.
|
|
// If we name all the functions init we don't have to
|
|
// maintain a list of which ones to run.
|
|
do := func(t *template.Template) {
|
|
fmt.Fprintln(out, `func init() {`)
|
|
for ; next(); a.reset() {
|
|
run(t, a, out)
|
|
}
|
|
fmt.Fprintln(out, `}`)
|
|
}
|
|
|
|
do(recv)
|
|
do(send)
|
|
do(recvOrder)
|
|
do(sendOrder)
|
|
do(nonblock)
|
|
|
|
fmt.Fprintln(out, "//", a.nreset, "cases")
|
|
out.Flush()
|
|
}
|
|
|
|
func run(t *template.Template, a interface{}, out io.Writer) {
|
|
if err := t.Execute(out, a); err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
type arg struct {
|
|
def bool
|
|
nreset int
|
|
}
|
|
|
|
func (a *arg) Maybe() bool {
|
|
return maybe()
|
|
}
|
|
|
|
func (a *arg) MaybeDefault() bool {
|
|
if a.def {
|
|
return false
|
|
}
|
|
a.def = maybe()
|
|
return a.def
|
|
}
|
|
|
|
func (a *arg) MustDefault() bool {
|
|
return !a.def
|
|
}
|
|
|
|
func (a *arg) reset() {
|
|
a.def = false
|
|
a.nreset++
|
|
}
|
|
|
|
const header = `// GENERATED BY select5.go; DO NOT EDIT
|
|
|
|
package main
|
|
|
|
// channel is buffered so test is single-goroutine.
|
|
// we are not interested in the concurrency aspects
|
|
// of select, just testing that the right calls happen.
|
|
var c = make(chan int, 1)
|
|
var nilch chan int
|
|
var n = 1
|
|
var x int
|
|
var i interface{}
|
|
var dummy = make(chan int)
|
|
var m = make(map[int]int)
|
|
var order = 0
|
|
|
|
func f(p *int) *int {
|
|
return p
|
|
}
|
|
|
|
// check order of operations by ensuring that
|
|
// successive calls to checkorder have increasing o values.
|
|
func checkorder(o int) {
|
|
if o <= order {
|
|
println("invalid order", o, "after", order)
|
|
panic("order")
|
|
}
|
|
order = o
|
|
}
|
|
|
|
func fc(c chan int, o int) chan int {
|
|
checkorder(o)
|
|
return c
|
|
}
|
|
|
|
func fp(p *int, o int) *int {
|
|
checkorder(o)
|
|
return p
|
|
}
|
|
|
|
func fn(n, o int) int {
|
|
checkorder(o)
|
|
return n
|
|
}
|
|
|
|
func die(x int) {
|
|
println("have", x, "want", n)
|
|
panic("chan")
|
|
}
|
|
|
|
func main() {
|
|
// everything happens in init funcs
|
|
}
|
|
`
|
|
|
|
func parse(name, s string) *template.Template {
|
|
t, err := template.New(name).Parse(s)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("%q: %s", name, err))
|
|
}
|
|
return t
|
|
}
|
|
|
|
var recv = parse("recv", `
|
|
{{/* Send n, receive it one way or another into x, check that they match. */}}
|
|
c <- n
|
|
{{if .Maybe}}
|
|
x = <-c
|
|
{{else}}
|
|
select {
|
|
{{/* Blocking or non-blocking, before the receive. */}}
|
|
{{/* The compiler implements two-case select where one is default with custom code, */}}
|
|
{{/* so test the default branch both before and after the send. */}}
|
|
{{if .MaybeDefault}}
|
|
default:
|
|
panic("nonblock")
|
|
{{end}}
|
|
{{/* Receive from c. Different cases are direct, indirect, :=, interface, and map assignment. */}}
|
|
{{if .Maybe}}
|
|
case x = <-c:
|
|
{{else}}{{if .Maybe}}
|
|
case *f(&x) = <-c:
|
|
{{else}}{{if .Maybe}}
|
|
case y := <-c:
|
|
x = y
|
|
{{else}}{{if .Maybe}}
|
|
case i = <-c:
|
|
x = i.(int)
|
|
{{else}}
|
|
case m[13] = <-c:
|
|
x = m[13]
|
|
{{end}}{{end}}{{end}}{{end}}
|
|
{{/* Blocking or non-blocking again, after the receive. */}}
|
|
{{if .MaybeDefault}}
|
|
default:
|
|
panic("nonblock")
|
|
{{end}}
|
|
{{/* Dummy send, receive to keep compiler from optimizing select. */}}
|
|
{{if .Maybe}}
|
|
case dummy <- 1:
|
|
panic("dummy send")
|
|
{{end}}
|
|
{{if .Maybe}}
|
|
case <-dummy:
|
|
panic("dummy receive")
|
|
{{end}}
|
|
{{/* Nil channel send, receive to keep compiler from optimizing select. */}}
|
|
{{if .Maybe}}
|
|
case nilch <- 1:
|
|
panic("nilch send")
|
|
{{end}}
|
|
{{if .Maybe}}
|
|
case <-nilch:
|
|
panic("nilch recv")
|
|
{{end}}
|
|
}
|
|
{{end}}
|
|
if x != n {
|
|
die(x)
|
|
}
|
|
n++
|
|
`)
|
|
|
|
var recvOrder = parse("recvOrder", `
|
|
{{/* Send n, receive it one way or another into x, check that they match. */}}
|
|
{{/* Check order of operations along the way by calling functions that check */}}
|
|
{{/* that the argument sequence is strictly increasing. */}}
|
|
order = 0
|
|
c <- n
|
|
{{if .Maybe}}
|
|
{{/* Outside of select, left-to-right rule applies. */}}
|
|
{{/* (Inside select, assignment waits until case is chosen, */}}
|
|
{{/* so right hand side happens before anything on left hand side. */}}
|
|
*fp(&x, 1) = <-fc(c, 2)
|
|
{{else}}{{if .Maybe}}
|
|
m[fn(13, 1)] = <-fc(c, 2)
|
|
x = m[13]
|
|
{{else}}
|
|
select {
|
|
{{/* Blocking or non-blocking, before the receive. */}}
|
|
{{/* The compiler implements two-case select where one is default with custom code, */}}
|
|
{{/* so test the default branch both before and after the send. */}}
|
|
{{if .MaybeDefault}}
|
|
default:
|
|
panic("nonblock")
|
|
{{end}}
|
|
{{/* Receive from c. Different cases are direct, indirect, :=, interface, and map assignment. */}}
|
|
{{if .Maybe}}
|
|
case *fp(&x, 100) = <-fc(c, 1):
|
|
{{else}}{{if .Maybe}}
|
|
case y := <-fc(c, 1):
|
|
x = y
|
|
{{else}}{{if .Maybe}}
|
|
case i = <-fc(c, 1):
|
|
x = i.(int)
|
|
{{else}}
|
|
case m[fn(13, 100)] = <-fc(c, 1):
|
|
x = m[13]
|
|
{{end}}{{end}}{{end}}
|
|
{{/* Blocking or non-blocking again, after the receive. */}}
|
|
{{if .MaybeDefault}}
|
|
default:
|
|
panic("nonblock")
|
|
{{end}}
|
|
{{/* Dummy send, receive to keep compiler from optimizing select. */}}
|
|
{{if .Maybe}}
|
|
case fc(dummy, 2) <- fn(1, 3):
|
|
panic("dummy send")
|
|
{{end}}
|
|
{{if .Maybe}}
|
|
case <-fc(dummy, 4):
|
|
panic("dummy receive")
|
|
{{end}}
|
|
{{/* Nil channel send, receive to keep compiler from optimizing select. */}}
|
|
{{if .Maybe}}
|
|
case fc(nilch, 5) <- fn(1, 6):
|
|
panic("nilch send")
|
|
{{end}}
|
|
{{if .Maybe}}
|
|
case <-fc(nilch, 7):
|
|
panic("nilch recv")
|
|
{{end}}
|
|
}
|
|
{{end}}{{end}}
|
|
if x != n {
|
|
die(x)
|
|
}
|
|
n++
|
|
`)
|
|
|
|
var send = parse("send", `
|
|
{{/* Send n one way or another, receive it into x, check that they match. */}}
|
|
{{if .Maybe}}
|
|
c <- n
|
|
{{else}}
|
|
select {
|
|
{{/* Blocking or non-blocking, before the receive (same reason as in recv). */}}
|
|
{{if .MaybeDefault}}
|
|
default:
|
|
panic("nonblock")
|
|
{{end}}
|
|
{{/* Send c <- n. No real special cases here, because no values come back */}}
|
|
{{/* from the send operation. */}}
|
|
case c <- n:
|
|
{{/* Blocking or non-blocking. */}}
|
|
{{if .MaybeDefault}}
|
|
default:
|
|
panic("nonblock")
|
|
{{end}}
|
|
{{/* Dummy send, receive to keep compiler from optimizing select. */}}
|
|
{{if .Maybe}}
|
|
case dummy <- 1:
|
|
panic("dummy send")
|
|
{{end}}
|
|
{{if .Maybe}}
|
|
case <-dummy:
|
|
panic("dummy receive")
|
|
{{end}}
|
|
{{/* Nil channel send, receive to keep compiler from optimizing select. */}}
|
|
{{if .Maybe}}
|
|
case nilch <- 1:
|
|
panic("nilch send")
|
|
{{end}}
|
|
{{if .Maybe}}
|
|
case <-nilch:
|
|
panic("nilch recv")
|
|
{{end}}
|
|
}
|
|
{{end}}
|
|
x = <-c
|
|
if x != n {
|
|
die(x)
|
|
}
|
|
n++
|
|
`)
|
|
|
|
var sendOrder = parse("sendOrder", `
|
|
{{/* Send n one way or another, receive it into x, check that they match. */}}
|
|
{{/* Check order of operations along the way by calling functions that check */}}
|
|
{{/* that the argument sequence is strictly increasing. */}}
|
|
order = 0
|
|
{{if .Maybe}}
|
|
fc(c, 1) <- fn(n, 2)
|
|
{{else}}
|
|
select {
|
|
{{/* Blocking or non-blocking, before the receive (same reason as in recv). */}}
|
|
{{if .MaybeDefault}}
|
|
default:
|
|
panic("nonblock")
|
|
{{end}}
|
|
{{/* Send c <- n. No real special cases here, because no values come back */}}
|
|
{{/* from the send operation. */}}
|
|
case fc(c, 1) <- fn(n, 2):
|
|
{{/* Blocking or non-blocking. */}}
|
|
{{if .MaybeDefault}}
|
|
default:
|
|
panic("nonblock")
|
|
{{end}}
|
|
{{/* Dummy send, receive to keep compiler from optimizing select. */}}
|
|
{{if .Maybe}}
|
|
case fc(dummy, 3) <- fn(1, 4):
|
|
panic("dummy send")
|
|
{{end}}
|
|
{{if .Maybe}}
|
|
case <-fc(dummy, 5):
|
|
panic("dummy receive")
|
|
{{end}}
|
|
{{/* Nil channel send, receive to keep compiler from optimizing select. */}}
|
|
{{if .Maybe}}
|
|
case fc(nilch, 6) <- fn(1, 7):
|
|
panic("nilch send")
|
|
{{end}}
|
|
{{if .Maybe}}
|
|
case <-fc(nilch, 8):
|
|
panic("nilch recv")
|
|
{{end}}
|
|
}
|
|
{{end}}
|
|
x = <-c
|
|
if x != n {
|
|
die(x)
|
|
}
|
|
n++
|
|
`)
|
|
|
|
var nonblock = parse("nonblock", `
|
|
x = n
|
|
{{/* Test various combinations of non-blocking operations. */}}
|
|
{{/* Receive assignments must not edit or even attempt to compute the address of the lhs. */}}
|
|
select {
|
|
{{if .MaybeDefault}}
|
|
default:
|
|
{{end}}
|
|
{{if .Maybe}}
|
|
case dummy <- 1:
|
|
panic("dummy <- 1")
|
|
{{end}}
|
|
{{if .Maybe}}
|
|
case nilch <- 1:
|
|
panic("nilch <- 1")
|
|
{{end}}
|
|
{{if .Maybe}}
|
|
case <-dummy:
|
|
panic("<-dummy")
|
|
{{end}}
|
|
{{if .Maybe}}
|
|
case x = <-dummy:
|
|
panic("<-dummy x")
|
|
{{end}}
|
|
{{if .Maybe}}
|
|
case **(**int)(nil) = <-dummy:
|
|
panic("<-dummy (and didn't crash saving result!)")
|
|
{{end}}
|
|
{{if .Maybe}}
|
|
case <-nilch:
|
|
panic("<-nilch")
|
|
{{end}}
|
|
{{if .Maybe}}
|
|
case x = <-nilch:
|
|
panic("<-nilch x")
|
|
{{end}}
|
|
{{if .Maybe}}
|
|
case **(**int)(nil) = <-nilch:
|
|
panic("<-nilch (and didn't crash saving result!)")
|
|
{{end}}
|
|
{{if .MustDefault}}
|
|
default:
|
|
{{end}}
|
|
}
|
|
if x != n {
|
|
die(x)
|
|
}
|
|
n++
|
|
`)
|
|
|
|
// Code for enumerating all possible paths through
|
|
// some logic. The logic should call choose(n) when
|
|
// it wants to choose between n possibilities.
|
|
// On successive runs through the logic, choose(n)
|
|
// will return 0, 1, ..., n-1. The helper maybe() is
|
|
// similar but returns true and then false.
|
|
//
|
|
// Given a function gen that generates an output
|
|
// using choose and maybe, code can generate all
|
|
// possible outputs using
|
|
//
|
|
// for next() {
|
|
// gen()
|
|
// }
|
|
|
|
type choice struct {
|
|
i, n int
|
|
}
|
|
|
|
var choices []choice
|
|
var cp int = -1
|
|
|
|
func maybe() bool {
|
|
return choose(2) == 0
|
|
}
|
|
|
|
func choose(n int) int {
|
|
if cp >= len(choices) {
|
|
// never asked this before: start with 0.
|
|
choices = append(choices, choice{0, n})
|
|
cp = len(choices)
|
|
return 0
|
|
}
|
|
// otherwise give recorded answer
|
|
if n != choices[cp].n {
|
|
panic("inconsistent choices")
|
|
}
|
|
i := choices[cp].i
|
|
cp++
|
|
return i
|
|
}
|
|
|
|
func next() bool {
|
|
if cp < 0 {
|
|
// start a new round
|
|
cp = 0
|
|
return true
|
|
}
|
|
|
|
// increment last choice sequence
|
|
cp = len(choices) - 1
|
|
for cp >= 0 && choices[cp].i == choices[cp].n-1 {
|
|
cp--
|
|
}
|
|
if cp < 0 {
|
|
choices = choices[:0]
|
|
return false
|
|
}
|
|
choices[cp].i++
|
|
choices = choices[:cp+1]
|
|
cp = 0
|
|
return true
|
|
}
|