Source file src/sync/cond_test.go

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

View as plain text