Black Lives Matter. Support the Equal Justice Initiative.

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