|  | // 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 ( | 
|  | "reflect" | 
|  | "runtime" | 
|  | . "sync" | 
|  | "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.Error("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.Error("want 3") | 
|  | } | 
|  | break | 
|  | } | 
|  | if x == 3 { | 
|  | break | 
|  | } | 
|  | c.L.Unlock() | 
|  | runtime.Gosched() | 
|  | c.L.Lock() | 
|  | } | 
|  | c.L.Unlock() | 
|  | done <- true | 
|  | }() | 
|  | <-done | 
|  | <-done | 
|  | <-done | 
|  | } | 
|  |  | 
|  | func TestCondSignalStealing(t *testing.T) { | 
|  | for iters := 0; iters < 1000; iters++ { | 
|  | var m Mutex | 
|  | cond := NewCond(&m) | 
|  |  | 
|  | // Start a waiter. | 
|  | ch := make(chan struct{}) | 
|  | go func() { | 
|  | m.Lock() | 
|  | ch <- struct{}{} | 
|  | cond.Wait() | 
|  | m.Unlock() | 
|  |  | 
|  | ch <- struct{}{} | 
|  | }() | 
|  |  | 
|  | <-ch | 
|  | m.Lock() | 
|  | m.Unlock() | 
|  |  | 
|  | // We know that the waiter is in the cond.Wait() call because we | 
|  | // synchronized with it, then acquired/released the mutex it was | 
|  | // holding when we synchronized. | 
|  | // | 
|  | // Start two goroutines that will race: one will broadcast on | 
|  | // the cond var, the other will wait on it. | 
|  | // | 
|  | // The new waiter may or may not get notified, but the first one | 
|  | // has to be notified. | 
|  | done := false | 
|  | go func() { | 
|  | cond.Broadcast() | 
|  | }() | 
|  |  | 
|  | go func() { | 
|  | m.Lock() | 
|  | for !done { | 
|  | cond.Wait() | 
|  | } | 
|  | m.Unlock() | 
|  | }() | 
|  |  | 
|  | // Check that the first waiter does get signaled. | 
|  | <-ch | 
|  |  | 
|  | // Release the second waiter in case it didn't get the | 
|  | // broadcast. | 
|  | m.Lock() | 
|  | done = true | 
|  | m.Unlock() | 
|  | cond.Broadcast() | 
|  | } | 
|  | } | 
|  |  | 
|  | 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() | 
|  | var c2 Cond | 
|  | reflect.ValueOf(&c2).Elem().Set(reflect.ValueOf(&c).Elem()) // c2 := c, hidden from vet | 
|  | 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 | 
|  | } | 
|  | } |