// 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 runtime_test import ( "runtime" "sync" "sync/atomic" "testing" ) func TestChanSendInterface(t *testing.T) { type mt struct{} m := &mt{} c := make(chan interface{}, 1) c <- m select { case c <- m: default: } select { case c <- m: case c <- &mt{}: default: } } func TestPseudoRandomSend(t *testing.T) { n := 100 c := make(chan int) l := make([]int, n) var m sync.Mutex m.Lock() go func() { for i := 0; i < n; i++ { runtime.Gosched() l[i] = <-c } m.Unlock() }() for i := 0; i < n; i++ { select { case c <- 0: case c <- 1: } } m.Lock() // wait n0 := 0 n1 := 0 for _, i := range l { n0 += (i + 1) % 2 n1 += i if n0 > n/10 && n1 > n/10 { return } } t.Errorf("Want pseudo random, got %d zeros and %d ones", n0, n1) } func TestMultiConsumer(t *testing.T) { const nwork = 23 const niter = 271828 pn := []int{2, 3, 7, 11, 13, 17, 19, 23, 27, 31} q := make(chan int, nwork*3) r := make(chan int, nwork*3) // workers var wg sync.WaitGroup for i := 0; i < nwork; i++ { wg.Add(1) go func(w int) { for v := range q { // mess with the fifo-ish nature of range if pn[w%len(pn)] == v { runtime.Gosched() } r <- v } wg.Done() }(i) } // feeder & closer expect := 0 go func() { for i := 0; i < niter; i++ { v := pn[i%len(pn)] expect += v q <- v } close(q) // no more work wg.Wait() // workers done close(r) // ... so there can be no more results }() // consume & check n := 0 s := 0 for v := range r { n++ s += v } if n != niter || s != expect { t.Errorf("Expected sum %d (got %d) from %d iter (saw %d)", expect, s, niter, n) } } func BenchmarkSelectUncontended(b *testing.B) { const CallsPerSched = 1000 procs := runtime.GOMAXPROCS(-1) N := int32(b.N / CallsPerSched) c := make(chan bool, procs) for p := 0; p < procs; p++ { go func() { myc1 := make(chan int, 1) myc2 := make(chan int, 1) myc1 <- 0 for atomic.AddInt32(&N, -1) >= 0 { for g := 0; g < CallsPerSched; g++ { select { case <-myc1: myc2 <- 0 case <-myc2: myc1 <- 0 } } } c <- true }() } for p := 0; p < procs; p++ { <-c } } func BenchmarkSelectContended(b *testing.B) { const CallsPerSched = 1000 procs := runtime.GOMAXPROCS(-1) N := int32(b.N / CallsPerSched) c := make(chan bool, procs) myc1 := make(chan int, procs) myc2 := make(chan int, procs) for p := 0; p < procs; p++ { myc1 <- 0 go func() { for atomic.AddInt32(&N, -1) >= 0 { for g := 0; g < CallsPerSched; g++ { select { case <-myc1: myc2 <- 0 case <-myc2: myc1 <- 0 } } } c <- true }() } for p := 0; p < procs; p++ { <-c } } func BenchmarkSelectNonblock(b *testing.B) { const CallsPerSched = 1000 procs := runtime.GOMAXPROCS(-1) N := int32(b.N / CallsPerSched) c := make(chan bool, procs) for p := 0; p < procs; p++ { go func() { myc1 := make(chan int) myc2 := make(chan int) myc3 := make(chan int, 1) myc4 := make(chan int, 1) for atomic.AddInt32(&N, -1) >= 0 { for g := 0; g < CallsPerSched; g++ { select { case <-myc1: default: } select { case myc2 <- 0: default: } select { case <-myc3: default: } select { case myc4 <- 0: default: } } } c <- true }() } for p := 0; p < procs; p++ { <-c } } func BenchmarkChanUncontended(b *testing.B) { const CallsPerSched = 1000 procs := runtime.GOMAXPROCS(-1) N := int32(b.N / CallsPerSched) c := make(chan bool, procs) for p := 0; p < procs; p++ { go func() { myc := make(chan int, CallsPerSched) for atomic.AddInt32(&N, -1) >= 0 { for g := 0; g < CallsPerSched; g++ { myc <- 0 } for g := 0; g < CallsPerSched; g++ { <-myc } } c <- true }() } for p := 0; p < procs; p++ { <-c } } func BenchmarkChanContended(b *testing.B) { const CallsPerSched = 1000 procs := runtime.GOMAXPROCS(-1) N := int32(b.N / CallsPerSched) c := make(chan bool, procs) myc := make(chan int, procs*CallsPerSched) for p := 0; p < procs; p++ { go func() { for atomic.AddInt32(&N, -1) >= 0 { for g := 0; g < CallsPerSched; g++ { myc <- 0 } for g := 0; g < CallsPerSched; g++ { <-myc } } c <- true }() } for p := 0; p < procs; p++ { <-c } } func BenchmarkChanSync(b *testing.B) { const CallsPerSched = 1000 procs := 2 N := int32(b.N / CallsPerSched / procs * procs) c := make(chan bool, procs) myc := make(chan int) for p := 0; p < procs; p++ { go func() { for { i := atomic.AddInt32(&N, -1) if i < 0 { break } for g := 0; g < CallsPerSched; g++ { if i%2 == 0 { <-myc myc <- 0 } else { myc <- 0 <-myc } } } c <- true }() } for p := 0; p < procs; p++ { <-c } } func benchmarkChanProdCons(b *testing.B, chanSize, localWork int) { const CallsPerSched = 1000 procs := runtime.GOMAXPROCS(-1) N := int32(b.N / CallsPerSched) c := make(chan bool, 2*procs) myc := make(chan int, chanSize) for p := 0; p < procs; p++ { go func() { foo := 0 for atomic.AddInt32(&N, -1) >= 0 { for g := 0; g < CallsPerSched; g++ { for i := 0; i < localWork; i++ { foo *= 2 foo /= 2 } myc <- 1 } } myc <- 0 c <- foo == 42 }() go func() { foo := 0 for { v := <-myc if v == 0 { break } for i := 0; i < localWork; i++ { foo *= 2 foo /= 2 } } c <- foo == 42 }() } for p := 0; p < procs; p++ { <-c <-c } } func BenchmarkChanProdCons0(b *testing.B) { benchmarkChanProdCons(b, 0, 0) } func BenchmarkChanProdCons10(b *testing.B) { benchmarkChanProdCons(b, 10, 0) } func BenchmarkChanProdCons100(b *testing.B) { benchmarkChanProdCons(b, 100, 0) } func BenchmarkChanProdConsWork0(b *testing.B) { benchmarkChanProdCons(b, 0, 100) } func BenchmarkChanProdConsWork10(b *testing.B) { benchmarkChanProdCons(b, 10, 100) } func BenchmarkChanProdConsWork100(b *testing.B) { benchmarkChanProdCons(b, 100, 100) } func BenchmarkChanCreation(b *testing.B) { const CallsPerSched = 1000 procs := runtime.GOMAXPROCS(-1) N := int32(b.N / CallsPerSched) c := make(chan bool, procs) for p := 0; p < procs; p++ { go func() { for atomic.AddInt32(&N, -1) >= 0 { for g := 0; g < CallsPerSched; g++ { myc := make(chan int, 1) myc <- 0 <-myc } } c <- true }() } for p := 0; p < procs; p++ { <-c } } func BenchmarkChanSem(b *testing.B) { type Empty struct{} c := make(chan Empty, 1) for i := 0; i < b.N; i++ { c <- Empty{} <-c } }