...

Source file src/sync/cond_test.go

Documentation: sync

		 1  // Copyright 2011 The Go Authors. All rights reserved.
		 2  // Use of this source code is governed by a BSD-style
		 3  // license that can be found in the LICENSE file.
		 4  
		 5  package sync_test
		 6  
		 7  import (
		 8  	"reflect"
		 9  	"runtime"
		10  	. "sync"
		11  	"testing"
		12  	"time"
		13  )
		14  
		15  func TestCondSignal(t *testing.T) {
		16  	var m Mutex
		17  	c := NewCond(&m)
		18  	n := 2
		19  	running := make(chan bool, n)
		20  	awake := make(chan bool, n)
		21  	for i := 0; i < n; i++ {
		22  		go func() {
		23  			m.Lock()
		24  			running <- true
		25  			c.Wait()
		26  			awake <- true
		27  			m.Unlock()
		28  		}()
		29  	}
		30  	for i := 0; i < n; i++ {
		31  		<-running // Wait for everyone to run.
		32  	}
		33  	for n > 0 {
		34  		select {
		35  		case <-awake:
		36  			t.Fatal("goroutine not asleep")
		37  		default:
		38  		}
		39  		m.Lock()
		40  		c.Signal()
		41  		m.Unlock()
		42  		<-awake // Will deadlock if no goroutine wakes up
		43  		select {
		44  		case <-awake:
		45  			t.Fatal("too many goroutines awake")
		46  		default:
		47  		}
		48  		n--
		49  	}
		50  	c.Signal()
		51  }
		52  
		53  func TestCondSignalGenerations(t *testing.T) {
		54  	var m Mutex
		55  	c := NewCond(&m)
		56  	n := 100
		57  	running := make(chan bool, n)
		58  	awake := make(chan int, n)
		59  	for i := 0; i < n; i++ {
		60  		go func(i int) {
		61  			m.Lock()
		62  			running <- true
		63  			c.Wait()
		64  			awake <- i
		65  			m.Unlock()
		66  		}(i)
		67  		if i > 0 {
		68  			a := <-awake
		69  			if a != i-1 {
		70  				t.Fatalf("wrong goroutine woke up: want %d, got %d", i-1, a)
		71  			}
		72  		}
		73  		<-running
		74  		m.Lock()
		75  		c.Signal()
		76  		m.Unlock()
		77  	}
		78  }
		79  
		80  func TestCondBroadcast(t *testing.T) {
		81  	var m Mutex
		82  	c := NewCond(&m)
		83  	n := 200
		84  	running := make(chan int, n)
		85  	awake := make(chan int, n)
		86  	exit := false
		87  	for i := 0; i < n; i++ {
		88  		go func(g int) {
		89  			m.Lock()
		90  			for !exit {
		91  				running <- g
		92  				c.Wait()
		93  				awake <- g
		94  			}
		95  			m.Unlock()
		96  		}(i)
		97  	}
		98  	for i := 0; i < n; i++ {
		99  		for i := 0; i < n; i++ {
	 100  			<-running // Will deadlock unless n are running.
	 101  		}
	 102  		if i == n-1 {
	 103  			m.Lock()
	 104  			exit = true
	 105  			m.Unlock()
	 106  		}
	 107  		select {
	 108  		case <-awake:
	 109  			t.Fatal("goroutine not asleep")
	 110  		default:
	 111  		}
	 112  		m.Lock()
	 113  		c.Broadcast()
	 114  		m.Unlock()
	 115  		seen := make([]bool, n)
	 116  		for i := 0; i < n; i++ {
	 117  			g := <-awake
	 118  			if seen[g] {
	 119  				t.Fatal("goroutine woke up twice")
	 120  			}
	 121  			seen[g] = true
	 122  		}
	 123  	}
	 124  	select {
	 125  	case <-running:
	 126  		t.Fatal("goroutine did not exit")
	 127  	default:
	 128  	}
	 129  	c.Broadcast()
	 130  }
	 131  
	 132  func TestRace(t *testing.T) {
	 133  	x := 0
	 134  	c := NewCond(&Mutex{})
	 135  	done := make(chan bool)
	 136  	go func() {
	 137  		c.L.Lock()
	 138  		x = 1
	 139  		c.Wait()
	 140  		if x != 2 {
	 141  			t.Error("want 2")
	 142  		}
	 143  		x = 3
	 144  		c.Signal()
	 145  		c.L.Unlock()
	 146  		done <- true
	 147  	}()
	 148  	go func() {
	 149  		c.L.Lock()
	 150  		for {
	 151  			if x == 1 {
	 152  				x = 2
	 153  				c.Signal()
	 154  				break
	 155  			}
	 156  			c.L.Unlock()
	 157  			runtime.Gosched()
	 158  			c.L.Lock()
	 159  		}
	 160  		c.L.Unlock()
	 161  		done <- true
	 162  	}()
	 163  	go func() {
	 164  		c.L.Lock()
	 165  		for {
	 166  			if x == 2 {
	 167  				c.Wait()
	 168  				if x != 3 {
	 169  					t.Error("want 3")
	 170  				}
	 171  				break
	 172  			}
	 173  			if x == 3 {
	 174  				break
	 175  			}
	 176  			c.L.Unlock()
	 177  			runtime.Gosched()
	 178  			c.L.Lock()
	 179  		}
	 180  		c.L.Unlock()
	 181  		done <- true
	 182  	}()
	 183  	<-done
	 184  	<-done
	 185  	<-done
	 186  }
	 187  
	 188  func TestCondSignalStealing(t *testing.T) {
	 189  	for iters := 0; iters < 1000; iters++ {
	 190  		var m Mutex
	 191  		cond := NewCond(&m)
	 192  
	 193  		// Start a waiter.
	 194  		ch := make(chan struct{})
	 195  		go func() {
	 196  			m.Lock()
	 197  			ch <- struct{}{}
	 198  			cond.Wait()
	 199  			m.Unlock()
	 200  
	 201  			ch <- struct{}{}
	 202  		}()
	 203  
	 204  		<-ch
	 205  		m.Lock()
	 206  		m.Unlock()
	 207  
	 208  		// We know that the waiter is in the cond.Wait() call because we
	 209  		// synchronized with it, then acquired/released the mutex it was
	 210  		// holding when we synchronized.
	 211  		//
	 212  		// Start two goroutines that will race: one will broadcast on
	 213  		// the cond var, the other will wait on it.
	 214  		//
	 215  		// The new waiter may or may not get notified, but the first one
	 216  		// has to be notified.
	 217  		done := false
	 218  		go func() {
	 219  			cond.Broadcast()
	 220  		}()
	 221  
	 222  		go func() {
	 223  			m.Lock()
	 224  			for !done {
	 225  				cond.Wait()
	 226  			}
	 227  			m.Unlock()
	 228  		}()
	 229  
	 230  		// Check that the first waiter does get signaled.
	 231  		select {
	 232  		case <-ch:
	 233  		case <-time.After(2 * time.Second):
	 234  			t.Fatalf("First waiter didn't get broadcast.")
	 235  		}
	 236  
	 237  		// Release the second waiter in case it didn't get the
	 238  		// broadcast.
	 239  		m.Lock()
	 240  		done = true
	 241  		m.Unlock()
	 242  		cond.Broadcast()
	 243  	}
	 244  }
	 245  
	 246  func TestCondCopy(t *testing.T) {
	 247  	defer func() {
	 248  		err := recover()
	 249  		if err == nil || err.(string) != "sync.Cond is copied" {
	 250  			t.Fatalf("got %v, expect sync.Cond is copied", err)
	 251  		}
	 252  	}()
	 253  	c := Cond{L: &Mutex{}}
	 254  	c.Signal()
	 255  	var c2 Cond
	 256  	reflect.ValueOf(&c2).Elem().Set(reflect.ValueOf(&c).Elem()) // c2 := c, hidden from vet
	 257  	c2.Signal()
	 258  }
	 259  
	 260  func BenchmarkCond1(b *testing.B) {
	 261  	benchmarkCond(b, 1)
	 262  }
	 263  
	 264  func BenchmarkCond2(b *testing.B) {
	 265  	benchmarkCond(b, 2)
	 266  }
	 267  
	 268  func BenchmarkCond4(b *testing.B) {
	 269  	benchmarkCond(b, 4)
	 270  }
	 271  
	 272  func BenchmarkCond8(b *testing.B) {
	 273  	benchmarkCond(b, 8)
	 274  }
	 275  
	 276  func BenchmarkCond16(b *testing.B) {
	 277  	benchmarkCond(b, 16)
	 278  }
	 279  
	 280  func BenchmarkCond32(b *testing.B) {
	 281  	benchmarkCond(b, 32)
	 282  }
	 283  
	 284  func benchmarkCond(b *testing.B, waiters int) {
	 285  	c := NewCond(&Mutex{})
	 286  	done := make(chan bool)
	 287  	id := 0
	 288  
	 289  	for routine := 0; routine < waiters+1; routine++ {
	 290  		go func() {
	 291  			for i := 0; i < b.N; i++ {
	 292  				c.L.Lock()
	 293  				if id == -1 {
	 294  					c.L.Unlock()
	 295  					break
	 296  				}
	 297  				id++
	 298  				if id == waiters+1 {
	 299  					id = 0
	 300  					c.Broadcast()
	 301  				} else {
	 302  					c.Wait()
	 303  				}
	 304  				c.L.Unlock()
	 305  			}
	 306  			c.L.Lock()
	 307  			id = -1
	 308  			c.Broadcast()
	 309  			c.L.Unlock()
	 310  			done <- true
	 311  		}()
	 312  	}
	 313  	for routine := 0; routine < waiters+1; routine++ {
	 314  		<-done
	 315  	}
	 316  }
	 317  

View as plain text