|  | // +build darwin linux | 
|  | // run | 
|  |  | 
|  | // Copyright 2014 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. | 
|  |  | 
|  | // Test that dequeueing from a pending channel doesn't | 
|  | // take linear time. | 
|  |  | 
|  | package main | 
|  |  | 
|  | import ( | 
|  | "fmt" | 
|  | "runtime" | 
|  | "time" | 
|  | ) | 
|  |  | 
|  | // checkLinear asserts that the running time of f(n) is in O(n). | 
|  | // tries is the initial number of iterations. | 
|  | func checkLinear(typ string, tries int, f func(n int)) { | 
|  | // Depending on the machine and OS, this test might be too fast | 
|  | // to measure with accurate enough granularity. On failure, | 
|  | // make it run longer, hoping that the timing granularity | 
|  | // is eventually sufficient. | 
|  |  | 
|  | timeF := func(n int) time.Duration { | 
|  | t1 := time.Now() | 
|  | f(n) | 
|  | return time.Since(t1) | 
|  | } | 
|  |  | 
|  | t0 := time.Now() | 
|  |  | 
|  | n := tries | 
|  | fails := 0 | 
|  | for { | 
|  | runtime.GC() | 
|  | t1 := timeF(n) | 
|  | runtime.GC() | 
|  | t2 := timeF(2 * n) | 
|  |  | 
|  | // should be 2x (linear); allow up to 3x | 
|  | if t2 < 3*t1 { | 
|  | if false { | 
|  | fmt.Println(typ, "\t", time.Since(t0)) | 
|  | } | 
|  | return | 
|  | } | 
|  | // If n ops run in under a second and the ratio | 
|  | // doesn't work out, make n bigger, trying to reduce | 
|  | // the effect that a constant amount of overhead has | 
|  | // on the computed ratio. | 
|  | if t1 < 1*time.Second { | 
|  | n *= 2 | 
|  | continue | 
|  | } | 
|  | // Once the test runs long enough for n ops, | 
|  | // try to get the right ratio at least once. | 
|  | // If five in a row all fail, give up. | 
|  | if fails++; fails >= 5 { | 
|  | panic(fmt.Sprintf("%s: too slow: %d channels: %v; %d channels: %v\n", | 
|  | typ, n, t1, 2*n, t2)) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | func main() { | 
|  | checkLinear("chanSelect", 1000, func(n int) { | 
|  | const messages = 10 | 
|  | c := make(chan bool) // global channel | 
|  | var a []chan bool    // local channels for each goroutine | 
|  | for i := 0; i < n; i++ { | 
|  | d := make(chan bool) | 
|  | a = append(a, d) | 
|  | go func() { | 
|  | for j := 0; j < messages; j++ { | 
|  | // queue ourselves on the global channel | 
|  | select { | 
|  | case <-c: | 
|  | case <-d: | 
|  | } | 
|  | } | 
|  | }() | 
|  | } | 
|  | for i := 0; i < messages; i++ { | 
|  | // wake each goroutine up, forcing it to dequeue and then enqueue | 
|  | // on the global channel. | 
|  | for _, d := range a { | 
|  | d <- true | 
|  | } | 
|  | } | 
|  | }) | 
|  | } |