...
Run Format

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  package sync_test
     5  
     6  import (
     7  	. "sync"
     8  
     9  	"runtime"
    10  	"testing"
    11  	"time"
    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  		select {
   231  		case <-ch:
   232  		case <-time.After(2 * time.Second):
   233  			t.Fatalf("First waiter didn't get broadcast.")
   234  		}
   235  
   236  		// Release the second waiter in case it didn't get the
   237  		// broadcast.
   238  		m.Lock()
   239  		done = true
   240  		m.Unlock()
   241  		cond.Broadcast()
   242  	}
   243  }
   244  
   245  func TestCondCopy(t *testing.T) {
   246  	defer func() {
   247  		err := recover()
   248  		if err == nil || err.(string) != "sync.Cond is copied" {
   249  			t.Fatalf("got %v, expect sync.Cond is copied", err)
   250  		}
   251  	}()
   252  	c := Cond{L: &Mutex{}}
   253  	c.Signal()
   254  	c2 := c
   255  	c2.Signal()
   256  }
   257  
   258  func BenchmarkCond1(b *testing.B) {
   259  	benchmarkCond(b, 1)
   260  }
   261  
   262  func BenchmarkCond2(b *testing.B) {
   263  	benchmarkCond(b, 2)
   264  }
   265  
   266  func BenchmarkCond4(b *testing.B) {
   267  	benchmarkCond(b, 4)
   268  }
   269  
   270  func BenchmarkCond8(b *testing.B) {
   271  	benchmarkCond(b, 8)
   272  }
   273  
   274  func BenchmarkCond16(b *testing.B) {
   275  	benchmarkCond(b, 16)
   276  }
   277  
   278  func BenchmarkCond32(b *testing.B) {
   279  	benchmarkCond(b, 32)
   280  }
   281  
   282  func benchmarkCond(b *testing.B, waiters int) {
   283  	c := NewCond(&Mutex{})
   284  	done := make(chan bool)
   285  	id := 0
   286  
   287  	for routine := 0; routine < waiters+1; routine++ {
   288  		go func() {
   289  			for i := 0; i < b.N; i++ {
   290  				c.L.Lock()
   291  				if id == -1 {
   292  					c.L.Unlock()
   293  					break
   294  				}
   295  				id++
   296  				if id == waiters+1 {
   297  					id = 0
   298  					c.Broadcast()
   299  				} else {
   300  					c.Wait()
   301  				}
   302  				c.L.Unlock()
   303  			}
   304  			c.L.Lock()
   305  			id = -1
   306  			c.Broadcast()
   307  			c.L.Unlock()
   308  			done <- true
   309  		}()
   310  	}
   311  	for routine := 0; routine < waiters+1; routine++ {
   312  		<-done
   313  	}
   314  }
   315  

View as plain text