256 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			256 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			Go
		
	
	
	
| // 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
 | |
| 	}
 | |
| }
 |