| // $G $D/$F.go && $L $F.$A && ./$A.out | |
| // Copyright 2009 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. | |
| package main | |
| import "os" | |
| import "runtime" | |
| import "sync" | |
| var randx int | |
| func nrand(n int) int { | |
| randx += 10007 | |
| if randx >= 1000000 { | |
| randx -= 1000000 | |
| } | |
| return randx % n | |
| } | |
| type Chan struct { | |
| sc, rc chan int // send and recv chan | |
| sv, rv int // send and recv seq | |
| } | |
| var ( | |
| nproc int | |
| nprocLock sync.Mutex | |
| cval int | |
| end int = 10000 | |
| totr, tots int | |
| totLock sync.Mutex | |
| nc *Chan | |
| ) | |
| func init() { | |
| nc = new(Chan) | |
| } | |
| func changeNproc(adjust int) int { | |
| nprocLock.Lock() | |
| nproc += adjust | |
| ret := nproc | |
| nprocLock.Unlock() | |
| return ret | |
| } | |
| func mkchan(c, n int) []*Chan { | |
| ca := make([]*Chan, n) | |
| for i := 0; i < n; i++ { | |
| cval = cval + 100 | |
| ch := new(Chan) | |
| ch.sc = make(chan int, c) | |
| ch.rc = ch.sc | |
| ch.sv = cval | |
| ch.rv = cval | |
| ca[i] = ch | |
| } | |
| return ca | |
| } | |
| func expect(v, v0 int) (newv int) { | |
| if v == v0 { | |
| if v%100 == 75 { | |
| return end | |
| } | |
| return v + 1 | |
| } | |
| print("got ", v, " expected ", v0+1, "\n") | |
| panic("fail") | |
| } | |
| func (c *Chan) send() bool { | |
| // print("send ", c.sv, "\n"); | |
| totLock.Lock() | |
| tots++ | |
| totLock.Unlock() | |
| c.sv = expect(c.sv, c.sv) | |
| if c.sv == end { | |
| c.sc = nil | |
| return true | |
| } | |
| return false | |
| } | |
| func send(c *Chan) { | |
| for { | |
| for r := nrand(10); r >= 0; r-- { | |
| runtime.Gosched() | |
| } | |
| c.sc <- c.sv | |
| if c.send() { | |
| break | |
| } | |
| } | |
| changeNproc(-1) | |
| } | |
| func (c *Chan) recv(v int) bool { | |
| // print("recv ", v, "\n"); | |
| totLock.Lock() | |
| totr++ | |
| totLock.Unlock() | |
| c.rv = expect(c.rv, v) | |
| if c.rv == end { | |
| c.rc = nil | |
| return true | |
| } | |
| return false | |
| } | |
| func recv(c *Chan) { | |
| var v int | |
| for { | |
| for r := nrand(10); r >= 0; r-- { | |
| runtime.Gosched() | |
| } | |
| v = <-c.rc | |
| if c.recv(v) { | |
| break | |
| } | |
| } | |
| changeNproc(-1) | |
| } | |
| func sel(r0, r1, r2, r3, s0, s1, s2, s3 *Chan) { | |
| var v int | |
| a := 0 // local chans running | |
| if r0.rc != nil { | |
| a++ | |
| } | |
| if r1.rc != nil { | |
| a++ | |
| } | |
| if r2.rc != nil { | |
| a++ | |
| } | |
| if r3.rc != nil { | |
| a++ | |
| } | |
| if s0.sc != nil { | |
| a++ | |
| } | |
| if s1.sc != nil { | |
| a++ | |
| } | |
| if s2.sc != nil { | |
| a++ | |
| } | |
| if s3.sc != nil { | |
| a++ | |
| } | |
| for { | |
| for r := nrand(5); r >= 0; r-- { | |
| runtime.Gosched() | |
| } | |
| select { | |
| case v = <-r0.rc: | |
| if r0.recv(v) { | |
| a-- | |
| } | |
| case v = <-r1.rc: | |
| if r1.recv(v) { | |
| a-- | |
| } | |
| case v = <-r2.rc: | |
| if r2.recv(v) { | |
| a-- | |
| } | |
| case v = <-r3.rc: | |
| if r3.recv(v) { | |
| a-- | |
| } | |
| case s0.sc <- s0.sv: | |
| if s0.send() { | |
| a-- | |
| } | |
| case s1.sc <- s1.sv: | |
| if s1.send() { | |
| a-- | |
| } | |
| case s2.sc <- s2.sv: | |
| if s2.send() { | |
| a-- | |
| } | |
| case s3.sc <- s3.sv: | |
| if s3.send() { | |
| a-- | |
| } | |
| } | |
| if a == 0 { | |
| break | |
| } | |
| } | |
| changeNproc(-1) | |
| } | |
| // direct send to direct recv | |
| func test1(c *Chan) { | |
| changeNproc(2) | |
| go send(c) | |
| go recv(c) | |
| } | |
| // direct send to select recv | |
| func test2(c int) { | |
| ca := mkchan(c, 4) | |
| changeNproc(4) | |
| go send(ca[0]) | |
| go send(ca[1]) | |
| go send(ca[2]) | |
| go send(ca[3]) | |
| changeNproc(1) | |
| go sel(ca[0], ca[1], ca[2], ca[3], nc, nc, nc, nc) | |
| } | |
| // select send to direct recv | |
| func test3(c int) { | |
| ca := mkchan(c, 4) | |
| changeNproc(4) | |
| go recv(ca[0]) | |
| go recv(ca[1]) | |
| go recv(ca[2]) | |
| go recv(ca[3]) | |
| changeNproc(1) | |
| go sel(nc, nc, nc, nc, ca[0], ca[1], ca[2], ca[3]) | |
| } | |
| // select send to select recv | |
| func test4(c int) { | |
| ca := mkchan(c, 4) | |
| changeNproc(2) | |
| go sel(nc, nc, nc, nc, ca[0], ca[1], ca[2], ca[3]) | |
| go sel(ca[0], ca[1], ca[2], ca[3], nc, nc, nc, nc) | |
| } | |
| func test5(c int) { | |
| ca := mkchan(c, 8) | |
| changeNproc(2) | |
| go sel(ca[4], ca[5], ca[6], ca[7], ca[0], ca[1], ca[2], ca[3]) | |
| go sel(ca[0], ca[1], ca[2], ca[3], ca[4], ca[5], ca[6], ca[7]) | |
| } | |
| func test6(c int) { | |
| ca := mkchan(c, 12) | |
| changeNproc(4) | |
| go send(ca[4]) | |
| go send(ca[5]) | |
| go send(ca[6]) | |
| go send(ca[7]) | |
| changeNproc(4) | |
| go recv(ca[8]) | |
| go recv(ca[9]) | |
| go recv(ca[10]) | |
| go recv(ca[11]) | |
| changeNproc(2) | |
| go sel(ca[4], ca[5], ca[6], ca[7], ca[0], ca[1], ca[2], ca[3]) | |
| go sel(ca[0], ca[1], ca[2], ca[3], ca[8], ca[9], ca[10], ca[11]) | |
| } | |
| // wait for outstanding tests to finish | |
| func wait() { | |
| runtime.Gosched() | |
| for changeNproc(0) != 0 { | |
| runtime.Gosched() | |
| } | |
| } | |
| // run all tests with specified buffer size | |
| func tests(c int) { | |
| ca := mkchan(c, 4) | |
| test1(ca[0]) | |
| test1(ca[1]) | |
| test1(ca[2]) | |
| test1(ca[3]) | |
| wait() | |
| test2(c) | |
| wait() | |
| test3(c) | |
| wait() | |
| test4(c) | |
| wait() | |
| test5(c) | |
| wait() | |
| test6(c) | |
| wait() | |
| } | |
| // run all test with 4 buffser sizes | |
| func main() { | |
| tests(0) | |
| tests(1) | |
| tests(10) | |
| tests(100) | |
| t := 4 * // buffer sizes | |
| (4*4 + // tests 1,2,3,4 channels | |
| 8 + // test 5 channels | |
| 12) * // test 6 channels | |
| 76 // sends/recvs on a channel | |
| if tots != t || totr != t { | |
| print("tots=", tots, " totr=", totr, " sb=", t, "\n") | |
| os.Exit(1) | |
| } | |
| os.Exit(0) | |
| } |