| // 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" |
| "time" |
| ) |
| |
| func TestChan(t *testing.T) { |
| defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4)) |
| N := 200 |
| if testing.Short() { |
| N = 20 |
| } |
| for chanCap := 0; chanCap < N; chanCap++ { |
| { |
| // Ensure that receive from empty chan blocks. |
| c := make(chan int, chanCap) |
| recv1 := false |
| go func() { |
| _ = <-c |
| recv1 = true |
| }() |
| recv2 := false |
| go func() { |
| _, _ = <-c |
| recv2 = true |
| }() |
| time.Sleep(time.Millisecond) |
| if recv1 || recv2 { |
| t.Fatalf("chan[%d]: receive from empty chan", chanCap) |
| } |
| // Ensure that non-blocking receive does not block. |
| select { |
| case _ = <-c: |
| t.Fatalf("chan[%d]: receive from empty chan", chanCap) |
| default: |
| } |
| select { |
| case _, _ = <-c: |
| t.Fatalf("chan[%d]: receive from empty chan", chanCap) |
| default: |
| } |
| c <- 0 |
| c <- 0 |
| } |
| |
| { |
| // Ensure that send to full chan blocks. |
| c := make(chan int, chanCap) |
| for i := 0; i < chanCap; i++ { |
| c <- i |
| } |
| sent := uint32(0) |
| go func() { |
| c <- 0 |
| atomic.StoreUint32(&sent, 1) |
| }() |
| time.Sleep(time.Millisecond) |
| if atomic.LoadUint32(&sent) != 0 { |
| t.Fatalf("chan[%d]: send to full chan", chanCap) |
| } |
| // Ensure that non-blocking send does not block. |
| select { |
| case c <- 0: |
| t.Fatalf("chan[%d]: send to full chan", chanCap) |
| default: |
| } |
| <-c |
| } |
| |
| { |
| // Ensure that we receive 0 from closed chan. |
| c := make(chan int, chanCap) |
| for i := 0; i < chanCap; i++ { |
| c <- i |
| } |
| close(c) |
| for i := 0; i < chanCap; i++ { |
| v := <-c |
| if v != i { |
| t.Fatalf("chan[%d]: received %v, expected %v", chanCap, v, i) |
| } |
| } |
| if v := <-c; v != 0 { |
| t.Fatalf("chan[%d]: received %v, expected %v", chanCap, v, 0) |
| } |
| if v, ok := <-c; v != 0 || ok { |
| t.Fatalf("chan[%d]: received %v/%v, expected %v/%v", chanCap, v, ok, 0, false) |
| } |
| } |
| |
| { |
| // Ensure that close unblocks receive. |
| c := make(chan int, chanCap) |
| done := make(chan bool) |
| go func() { |
| v, ok := <-c |
| done <- v == 0 && ok == false |
| }() |
| time.Sleep(time.Millisecond) |
| close(c) |
| if !<-done { |
| t.Fatalf("chan[%d]: received non zero from closed chan", chanCap) |
| } |
| } |
| |
| { |
| // Send 100 integers, |
| // ensure that we receive them non-corrupted in FIFO order. |
| c := make(chan int, chanCap) |
| go func() { |
| for i := 0; i < 100; i++ { |
| c <- i |
| } |
| }() |
| for i := 0; i < 100; i++ { |
| v := <-c |
| if v != i { |
| t.Fatalf("chan[%d]: received %v, expected %v", chanCap, v, i) |
| } |
| } |
| |
| // Same, but using recv2. |
| go func() { |
| for i := 0; i < 100; i++ { |
| c <- i |
| } |
| }() |
| for i := 0; i < 100; i++ { |
| v, ok := <-c |
| if !ok { |
| t.Fatalf("chan[%d]: receive failed, expected %v", chanCap, i) |
| } |
| if v != i { |
| t.Fatalf("chan[%d]: received %v, expected %v", chanCap, v, i) |
| } |
| } |
| |
| // Send 1000 integers in 4 goroutines, |
| // ensure that we receive what we send. |
| const P = 4 |
| const L = 1000 |
| for p := 0; p < P; p++ { |
| go func() { |
| for i := 0; i < L; i++ { |
| c <- i |
| } |
| }() |
| } |
| done := make(chan map[int]int) |
| for p := 0; p < P; p++ { |
| go func() { |
| recv := make(map[int]int) |
| for i := 0; i < L; i++ { |
| v := <-c |
| recv[v] = recv[v] + 1 |
| } |
| done <- recv |
| }() |
| } |
| recv := make(map[int]int) |
| for p := 0; p < P; p++ { |
| for k, v := range <-done { |
| recv[k] = recv[k] + v |
| } |
| } |
| if len(recv) != L { |
| t.Fatalf("chan[%d]: received %v values, expected %v", chanCap, len(recv), L) |
| } |
| for _, v := range recv { |
| if v != P { |
| t.Fatalf("chan[%d]: received %v values, expected %v", chanCap, v, P) |
| } |
| } |
| } |
| |
| { |
| // Test len/cap. |
| c := make(chan int, chanCap) |
| if len(c) != 0 || cap(c) != chanCap { |
| t.Fatalf("chan[%d]: bad len/cap, expect %v/%v, got %v/%v", chanCap, 0, chanCap, len(c), cap(c)) |
| } |
| for i := 0; i < chanCap; i++ { |
| c <- i |
| } |
| if len(c) != chanCap || cap(c) != chanCap { |
| t.Fatalf("chan[%d]: bad len/cap, expect %v/%v, got %v/%v", chanCap, chanCap, chanCap, len(c), cap(c)) |
| } |
| } |
| |
| } |
| } |
| |
| func TestSelfSelect(t *testing.T) { |
| // Ensure that send/recv on the same chan in select |
| // does not crash nor deadlock. |
| defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2)) |
| for _, chanCap := range []int{0, 10} { |
| var wg sync.WaitGroup |
| wg.Add(2) |
| c := make(chan int, chanCap) |
| for p := 0; p < 2; p++ { |
| p := p |
| go func() { |
| defer wg.Done() |
| for i := 0; i < 1000; i++ { |
| if p == 0 || i%2 == 0 { |
| select { |
| case c <- p: |
| case v := <-c: |
| if chanCap == 0 && v == p { |
| t.Fatalf("self receive") |
| } |
| } |
| } else { |
| select { |
| case v := <-c: |
| if chanCap == 0 && v == p { |
| t.Fatalf("self receive") |
| } |
| case c <- p: |
| } |
| } |
| } |
| }() |
| } |
| wg.Wait() |
| } |
| } |
| |
| func TestSelectStress(t *testing.T) { |
| defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(10)) |
| var c [4]chan int |
| c[0] = make(chan int) |
| c[1] = make(chan int) |
| c[2] = make(chan int, 2) |
| c[3] = make(chan int, 3) |
| N := int(1e5) |
| if testing.Short() { |
| N /= 10 |
| } |
| // There are 4 goroutines that send N values on each of the chans, |
| // + 4 goroutines that receive N values on each of the chans, |
| // + 1 goroutine that sends N values on each of the chans in a single select, |
| // + 1 goroutine that receives N values on each of the chans in a single select. |
| // All these sends, receives and selects interact chaotically at runtime, |
| // but we are careful that this whole construct does not deadlock. |
| var wg sync.WaitGroup |
| wg.Add(10) |
| for k := 0; k < 4; k++ { |
| k := k |
| go func() { |
| for i := 0; i < N; i++ { |
| c[k] <- 0 |
| } |
| wg.Done() |
| }() |
| go func() { |
| for i := 0; i < N; i++ { |
| <-c[k] |
| } |
| wg.Done() |
| }() |
| } |
| go func() { |
| var n [4]int |
| c1 := c |
| for i := 0; i < 4*N; i++ { |
| select { |
| case c1[3] <- 0: |
| n[3]++ |
| if n[3] == N { |
| c1[3] = nil |
| } |
| case c1[2] <- 0: |
| n[2]++ |
| if n[2] == N { |
| c1[2] = nil |
| } |
| case c1[0] <- 0: |
| n[0]++ |
| if n[0] == N { |
| c1[0] = nil |
| } |
| case c1[1] <- 0: |
| n[1]++ |
| if n[1] == N { |
| c1[1] = nil |
| } |
| } |
| } |
| wg.Done() |
| }() |
| go func() { |
| var n [4]int |
| c1 := c |
| for i := 0; i < 4*N; i++ { |
| select { |
| case <-c1[0]: |
| n[0]++ |
| if n[0] == N { |
| c1[0] = nil |
| } |
| case <-c1[1]: |
| n[1]++ |
| if n[1] == N { |
| c1[1] = nil |
| } |
| case <-c1[2]: |
| n[2]++ |
| if n[2] == N { |
| c1[2] = nil |
| } |
| case <-c1[3]: |
| n[3]++ |
| if n[3] == N { |
| c1[3] = nil |
| } |
| } |
| } |
| wg.Done() |
| }() |
| wg.Wait() |
| } |
| |
| 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 |
| for _, chanCap := range []int{0, n} { |
| c := make(chan int, chanCap) |
| 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 <- 1: |
| case c <- 0: |
| } |
| } |
| m.Lock() // wait |
| n0 := 0 |
| n1 := 0 |
| for _, i := range l { |
| n0 += (i + 1) % 2 |
| n1 += i |
| } |
| if n0 <= n/10 || n1 <= n/10 { |
| t.Errorf("Want pseudorandom, got %d zeros and %d ones (chan cap %d)", n0, n1, chanCap) |
| } |
| } |
| } |
| |
| 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 BenchmarkChanNonblocking(b *testing.B) { |
| myc := make(chan int) |
| b.RunParallel(func(pb *testing.PB) { |
| for pb.Next() { |
| select { |
| case <-myc: |
| default: |
| } |
| } |
| }) |
| } |
| |
| func BenchmarkSelectUncontended(b *testing.B) { |
| b.RunParallel(func(pb *testing.PB) { |
| myc1 := make(chan int, 1) |
| myc2 := make(chan int, 1) |
| myc1 <- 0 |
| for pb.Next() { |
| select { |
| case <-myc1: |
| myc2 <- 0 |
| case <-myc2: |
| myc1 <- 0 |
| } |
| } |
| }) |
| } |
| |
| func BenchmarkSelectContended(b *testing.B) { |
| procs := runtime.GOMAXPROCS(0) |
| myc1 := make(chan int, procs) |
| myc2 := make(chan int, procs) |
| b.RunParallel(func(pb *testing.PB) { |
| myc1 <- 0 |
| for pb.Next() { |
| select { |
| case <-myc1: |
| myc2 <- 0 |
| case <-myc2: |
| myc1 <- 0 |
| } |
| } |
| }) |
| } |
| |
| func BenchmarkSelectNonblock(b *testing.B) { |
| b.RunParallel(func(pb *testing.PB) { |
| myc1 := make(chan int) |
| myc2 := make(chan int) |
| myc3 := make(chan int, 1) |
| myc4 := make(chan int, 1) |
| for pb.Next() { |
| select { |
| case <-myc1: |
| default: |
| } |
| select { |
| case myc2 <- 0: |
| default: |
| } |
| select { |
| case <-myc3: |
| default: |
| } |
| select { |
| case myc4 <- 0: |
| default: |
| } |
| } |
| }) |
| } |
| |
| func BenchmarkChanUncontended(b *testing.B) { |
| const C = 100 |
| b.RunParallel(func(pb *testing.PB) { |
| myc := make(chan int, C) |
| for pb.Next() { |
| for i := 0; i < C; i++ { |
| myc <- 0 |
| } |
| for i := 0; i < C; i++ { |
| <-myc |
| } |
| } |
| }) |
| } |
| |
| func BenchmarkChanContended(b *testing.B) { |
| const C = 100 |
| myc := make(chan int, C*runtime.GOMAXPROCS(0)) |
| b.RunParallel(func(pb *testing.PB) { |
| for pb.Next() { |
| for i := 0; i < C; i++ { |
| myc <- 0 |
| } |
| for i := 0; i < C; i++ { |
| <-myc |
| } |
| } |
| }) |
| } |
| |
| 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 BenchmarkSelectProdCons(b *testing.B) { |
| const CallsPerSched = 1000 |
| procs := runtime.GOMAXPROCS(-1) |
| N := int32(b.N / CallsPerSched) |
| c := make(chan bool, 2*procs) |
| myc := make(chan int, 128) |
| myclose := make(chan bool) |
| for p := 0; p < procs; p++ { |
| go func() { |
| // Producer: sends to myc. |
| foo := 0 |
| // Intended to not fire during benchmarking. |
| mytimer := time.After(time.Hour) |
| for atomic.AddInt32(&N, -1) >= 0 { |
| for g := 0; g < CallsPerSched; g++ { |
| // Model some local work. |
| for i := 0; i < 100; i++ { |
| foo *= 2 |
| foo /= 2 |
| } |
| select { |
| case myc <- 1: |
| case <-mytimer: |
| case <-myclose: |
| } |
| } |
| } |
| myc <- 0 |
| c <- foo == 42 |
| }() |
| go func() { |
| // Consumer: receives from myc. |
| foo := 0 |
| // Intended to not fire during benchmarking. |
| mytimer := time.After(time.Hour) |
| loop: |
| for { |
| select { |
| case v := <-myc: |
| if v == 0 { |
| break loop |
| } |
| case <-mytimer: |
| case <-myclose: |
| } |
| // Model some local work. |
| for i := 0; i < 100; i++ { |
| foo *= 2 |
| foo /= 2 |
| } |
| } |
| c <- foo == 42 |
| }() |
| } |
| for p := 0; p < procs; p++ { |
| <-c |
| <-c |
| } |
| } |
| |
| func BenchmarkChanCreation(b *testing.B) { |
| b.RunParallel(func(pb *testing.PB) { |
| for pb.Next() { |
| myc := make(chan int, 1) |
| myc <- 0 |
| <-myc |
| } |
| }) |
| } |
| |
| func BenchmarkChanSem(b *testing.B) { |
| type Empty struct{} |
| myc := make(chan Empty, runtime.GOMAXPROCS(0)) |
| b.RunParallel(func(pb *testing.PB) { |
| for pb.Next() { |
| myc <- Empty{} |
| <-myc |
| } |
| }) |
| } |