| // 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. | |
| package sync_test | |
| import ( | |
| . "sync" | |
| "runtime" | |
| "testing" | |
| ) | |
| func TestCondSignal(t *testing.T) { | |
| var m Mutex | |
| c := NewCond(&m) | |
| n := 2 | |
| running := make(chan bool, n) | |
| awake := make(chan bool, n) | |
| for i := 0; i < n; i++ { | |
| go func() { | |
| m.Lock() | |
| running <- true | |
| c.Wait() | |
| awake <- true | |
| m.Unlock() | |
| }() | |
| } | |
| for i := 0; i < n; i++ { | |
| <-running // Wait for everyone to run. | |
| } | |
| for n > 0 { | |
| select { | |
| case <-awake: | |
| t.Fatal("goroutine not asleep") | |
| default: | |
| } | |
| m.Lock() | |
| c.Signal() | |
| m.Unlock() | |
| <-awake // Will deadlock if no goroutine wakes up | |
| select { | |
| case <-awake: | |
| t.Fatal("too many goroutines awake") | |
| default: | |
| } | |
| n-- | |
| } | |
| c.Signal() | |
| } | |
| func TestCondSignalGenerations(t *testing.T) { | |
| var m Mutex | |
| c := NewCond(&m) | |
| n := 100 | |
| running := make(chan bool, n) | |
| awake := make(chan int, n) | |
| for i := 0; i < n; i++ { | |
| go func(i int) { | |
| m.Lock() | |
| running <- true | |
| c.Wait() | |
| awake <- i | |
| m.Unlock() | |
| }(i) | |
| if i > 0 { | |
| a := <-awake | |
| if a != i-1 { | |
| t.Fatalf("wrong goroutine woke up: want %d, got %d", i-1, a) | |
| } | |
| } | |
| <-running | |
| m.Lock() | |
| c.Signal() | |
| m.Unlock() | |
| } | |
| } | |
| func TestCondBroadcast(t *testing.T) { | |
| var m Mutex | |
| c := NewCond(&m) | |
| n := 200 | |
| running := make(chan int, n) | |
| awake := make(chan int, n) | |
| exit := false | |
| for i := 0; i < n; i++ { | |
| go func(g int) { | |
| m.Lock() | |
| for !exit { | |
| running <- g | |
| c.Wait() | |
| awake <- g | |
| } | |
| m.Unlock() | |
| }(i) | |
| } | |
| for i := 0; i < n; i++ { | |
| for i := 0; i < n; i++ { | |
| <-running // Will deadlock unless n are running. | |
| } | |
| if i == n-1 { | |
| m.Lock() | |
| exit = true | |
| m.Unlock() | |
| } | |
| select { | |
| case <-awake: | |
| t.Fatal("goroutine not asleep") | |
| default: | |
| } | |
| m.Lock() | |
| c.Broadcast() | |
| m.Unlock() | |
| seen := make([]bool, n) | |
| for i := 0; i < n; i++ { | |
| g := <-awake | |
| if seen[g] { | |
| t.Fatal("goroutine woke up twice") | |
| } | |
| seen[g] = true | |
| } | |
| } | |
| select { | |
| case <-running: | |
| t.Fatal("goroutine did not exit") | |
| default: | |
| } | |
| c.Broadcast() | |
| } | |
| func TestRace(t *testing.T) { | |
| x := 0 | |
| c := NewCond(&Mutex{}) | |
| done := make(chan bool) | |
| go func() { | |
| c.L.Lock() | |
| x = 1 | |
| c.Wait() | |
| if x != 2 { | |
| t.Fatal("want 2") | |
| } | |
| x = 3 | |
| c.Signal() | |
| c.L.Unlock() | |
| done <- true | |
| }() | |
| go func() { | |
| c.L.Lock() | |
| for { | |
| if x == 1 { | |
| x = 2 | |
| c.Signal() | |
| break | |
| } | |
| c.L.Unlock() | |
| runtime.Gosched() | |
| c.L.Lock() | |
| } | |
| c.L.Unlock() | |
| done <- true | |
| }() | |
| go func() { | |
| c.L.Lock() | |
| for { | |
| if x == 2 { | |
| c.Wait() | |
| if x != 3 { | |
| t.Fatal("want 3") | |
| } | |
| break | |
| } | |
| if x == 3 { | |
| break | |
| } | |
| c.L.Unlock() | |
| runtime.Gosched() | |
| c.L.Lock() | |
| } | |
| c.L.Unlock() | |
| done <- true | |
| }() | |
| <-done | |
| <-done | |
| <-done | |
| } | |
| func TestCondCopy(t *testing.T) { | |
| defer func() { | |
| err := recover() | |
| if err == nil || err.(string) != "sync.Cond is copied" { | |
| t.Fatalf("got %v, expect sync.Cond is copied", err) | |
| } | |
| }() | |
| c := Cond{L: &Mutex{}} | |
| c.Signal() | |
| c2 := c | |
| c2.Signal() | |
| } | |
| func BenchmarkCond1(b *testing.B) { | |
| benchmarkCond(b, 1) | |
| } | |
| func BenchmarkCond2(b *testing.B) { | |
| benchmarkCond(b, 2) | |
| } | |
| func BenchmarkCond4(b *testing.B) { | |
| benchmarkCond(b, 4) | |
| } | |
| func BenchmarkCond8(b *testing.B) { | |
| benchmarkCond(b, 8) | |
| } | |
| func BenchmarkCond16(b *testing.B) { | |
| benchmarkCond(b, 16) | |
| } | |
| func BenchmarkCond32(b *testing.B) { | |
| benchmarkCond(b, 32) | |
| } | |
| func benchmarkCond(b *testing.B, waiters int) { | |
| c := NewCond(&Mutex{}) | |
| done := make(chan bool) | |
| id := 0 | |
| for routine := 0; routine < waiters+1; routine++ { | |
| go func() { | |
| for i := 0; i < b.N; i++ { | |
| c.L.Lock() | |
| if id == -1 { | |
| c.L.Unlock() | |
| break | |
| } | |
| id++ | |
| if id == waiters+1 { | |
| id = 0 | |
| c.Broadcast() | |
| } else { | |
| c.Wait() | |
| } | |
| c.L.Unlock() | |
| } | |
| c.L.Lock() | |
| id = -1 | |
| c.Broadcast() | |
| c.L.Unlock() | |
| done <- true | |
| }() | |
| } | |
| for routine := 0; routine < waiters+1; routine++ { | |
| <-done | |
| } | |
| } |