| // 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" | 
 | 	"time" | 
 | ) | 
 |  | 
 | 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. | 
 | 		select { | 
 | 		case <-ch: | 
 | 		case <-time.After(2 * time.Second): | 
 | 			t.Fatalf("First waiter didn't get broadcast.") | 
 | 		} | 
 |  | 
 | 		// 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 | 
 | 	} | 
 | } |