| // $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. |
| // Only doing 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" |
| "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(s string) *template.Template { |
| t := template.New(nil) |
| t.SetDelims("〈", "〉") |
| if err := t.Parse(s); err != nil { |
| panic(s) |
| } |
| return t |
| } |
| |
| var recv = parse(` |
| 〈# Send n, receive it one way or another into x, check that they match.〉 |
| c <- n |
| 〈.section Maybe〉 |
| x = <-c |
| 〈.or〉 |
| 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.〉 |
| 〈.section MaybeDefault〉 |
| default: |
| panic("nonblock") |
| 〈.end〉 |
| 〈# Receive from c. Different cases are direct, indirect, :=, interface, and map assignment.〉 |
| 〈.section Maybe〉 |
| case x = <-c: |
| 〈.or〉〈.section Maybe〉 |
| case *f(&x) = <-c: |
| 〈.or〉〈.section Maybe〉 |
| case y := <-c: |
| x = y |
| 〈.or〉〈.section Maybe〉 |
| case i = <-c: |
| x = i.(int) |
| 〈.or〉 |
| case m[13] = <-c: |
| x = m[13] |
| 〈.end〉〈.end〉〈.end〉〈.end〉 |
| 〈# Blocking or non-blocking again, after the receive.〉 |
| 〈.section MaybeDefault〉 |
| default: |
| panic("nonblock") |
| 〈.end〉 |
| 〈# Dummy send, receive to keep compiler from optimizing select.〉 |
| 〈.section Maybe〉 |
| case dummy <- 1: |
| panic("dummy send") |
| 〈.end〉 |
| 〈.section Maybe〉 |
| case <-dummy: |
| panic("dummy receive") |
| 〈.end〉 |
| 〈# Nil channel send, receive to keep compiler from optimizing select.〉 |
| 〈.section Maybe〉 |
| case nilch <- 1: |
| panic("nilch send") |
| 〈.end〉 |
| 〈.section Maybe〉 |
| case <-nilch: |
| panic("nilch recv") |
| 〈.end〉 |
| } |
| 〈.end〉 |
| if x != n { |
| die(x) |
| } |
| n++ |
| `) |
| |
| var recvOrder = parse(` |
| 〈# 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 |
| 〈.section 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) |
| 〈.or〉〈.section Maybe〉 |
| m[fn(13, 1)] = <-fc(c, 2) |
| x = m[13] |
| 〈.or〉 |
| 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.〉 |
| 〈.section MaybeDefault〉 |
| default: |
| panic("nonblock") |
| 〈.end〉 |
| 〈# Receive from c. Different cases are direct, indirect, :=, interface, and map assignment.〉 |
| 〈.section Maybe〉 |
| case *fp(&x, 100) = <-fc(c, 1): |
| 〈.or〉〈.section Maybe〉 |
| case y := <-fc(c, 1): |
| x = y |
| 〈.or〉〈.section Maybe〉 |
| case i = <-fc(c, 1): |
| x = i.(int) |
| 〈.or〉 |
| case m[fn(13, 100)] = <-fc(c, 1): |
| x = m[13] |
| 〈.end〉〈.end〉〈.end〉 |
| 〈# Blocking or non-blocking again, after the receive.〉 |
| 〈.section MaybeDefault〉 |
| default: |
| panic("nonblock") |
| 〈.end〉 |
| 〈# Dummy send, receive to keep compiler from optimizing select.〉 |
| 〈.section Maybe〉 |
| case fc(dummy, 2) <- fn(1, 3): |
| panic("dummy send") |
| 〈.end〉 |
| 〈.section Maybe〉 |
| case <-fc(dummy, 4): |
| panic("dummy receive") |
| 〈.end〉 |
| 〈# Nil channel send, receive to keep compiler from optimizing select.〉 |
| 〈.section Maybe〉 |
| case fc(nilch, 5) <- fn(1, 6): |
| panic("nilch send") |
| 〈.end〉 |
| 〈.section Maybe〉 |
| case <-fc(nilch, 7): |
| panic("nilch recv") |
| 〈.end〉 |
| } |
| 〈.end〉〈.end〉 |
| if x != n { |
| die(x) |
| } |
| n++ |
| `) |
| |
| var send = parse(` |
| 〈# Send n one way or another, receive it into x, check that they match.〉 |
| 〈.section Maybe〉 |
| c <- n |
| 〈.or〉 |
| select { |
| 〈# Blocking or non-blocking, before the receive (same reason as in recv).〉 |
| 〈.section 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.〉 |
| 〈.section MaybeDefault〉 |
| default: |
| panic("nonblock") |
| 〈.end〉 |
| 〈# Dummy send, receive to keep compiler from optimizing select.〉 |
| 〈.section Maybe〉 |
| case dummy <- 1: |
| panic("dummy send") |
| 〈.end〉 |
| 〈.section Maybe〉 |
| case <-dummy: |
| panic("dummy receive") |
| 〈.end〉 |
| 〈# Nil channel send, receive to keep compiler from optimizing select.〉 |
| 〈.section Maybe〉 |
| case nilch <- 1: |
| panic("nilch send") |
| 〈.end〉 |
| 〈.section Maybe〉 |
| case <-nilch: |
| panic("nilch recv") |
| 〈.end〉 |
| } |
| 〈.end〉 |
| x = <-c |
| if x != n { |
| die(x) |
| } |
| n++ |
| `) |
| |
| var sendOrder = parse(` |
| 〈# 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 |
| 〈.section Maybe〉 |
| fc(c, 1) <- fn(n, 2) |
| 〈.or〉 |
| select { |
| 〈# Blocking or non-blocking, before the receive (same reason as in recv).〉 |
| 〈.section 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.〉 |
| 〈.section MaybeDefault〉 |
| default: |
| panic("nonblock") |
| 〈.end〉 |
| 〈# Dummy send, receive to keep compiler from optimizing select.〉 |
| 〈.section Maybe〉 |
| case fc(dummy, 3) <- fn(1, 4): |
| panic("dummy send") |
| 〈.end〉 |
| 〈.section Maybe〉 |
| case <-fc(dummy, 5): |
| panic("dummy receive") |
| 〈.end〉 |
| 〈# Nil channel send, receive to keep compiler from optimizing select.〉 |
| 〈.section Maybe〉 |
| case fc(nilch, 6) <- fn(1, 7): |
| panic("nilch send") |
| 〈.end〉 |
| 〈.section Maybe〉 |
| case <-fc(nilch, 8): |
| panic("nilch recv") |
| 〈.end〉 |
| } |
| 〈.end〉 |
| x = <-c |
| if x != n { |
| die(x) |
| } |
| n++ |
| `) |
| |
| var nonblock = parse(` |
| 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 { |
| 〈.section MaybeDefault〉 |
| default: |
| 〈.end〉 |
| 〈.section Maybe〉 |
| case dummy <- 1: |
| panic("dummy <- 1") |
| 〈.end〉 |
| 〈.section Maybe〉 |
| case nilch <- 1: |
| panic("nilch <- 1") |
| 〈.end〉 |
| 〈.section Maybe〉 |
| case <-dummy: |
| panic("<-dummy") |
| 〈.end〉 |
| 〈.section Maybe〉 |
| case x = <-dummy: |
| panic("<-dummy x") |
| 〈.end〉 |
| 〈.section Maybe〉 |
| case **(**int)(nil) = <-dummy: |
| panic("<-dummy (and didn't crash saving result!)") |
| 〈.end〉 |
| 〈.section Maybe〉 |
| case <-nilch: |
| panic("<-nilch") |
| 〈.end〉 |
| 〈.section Maybe〉 |
| case x = <-nilch: |
| panic("<-nilch x") |
| 〈.end〉 |
| 〈.section Maybe〉 |
| case **(**int)(nil) = <-nilch: |
| panic("<-nilch (and didn't crash saving result!)") |
| 〈.end〉 |
| 〈.section 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 |
| } |
| |