Home | History | Annotate | Download | only in 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