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 )
     12 
     13 func TestCondSignal(t *testing.T) {
     14 	var m Mutex
     15 	c := NewCond(&m)
     16 	n := 2
     17 	running := make(chan bool, n)
     18 	awake := make(chan bool, n)
     19 	for i := 0; i < n; i++ {
     20 		go func() {
     21 			m.Lock()
     22 			running <- true
     23 			c.Wait()
     24 			awake <- true
     25 			m.Unlock()
     26 		}()
     27 	}
     28 	for i := 0; i < n; i++ {
     29 		<-running // Wait for everyone to run.
     30 	}
     31 	for n > 0 {
     32 		select {
     33 		case <-awake:
     34 			t.Fatal("goroutine not asleep")
     35 		default:
     36 		}
     37 		m.Lock()
     38 		c.Signal()
     39 		m.Unlock()
     40 		<-awake // Will deadlock if no goroutine wakes up
     41 		select {
     42 		case <-awake:
     43 			t.Fatal("too many goroutines awake")
     44 		default:
     45 		}
     46 		n--
     47 	}
     48 	c.Signal()
     49 }
     50 
     51 func TestCondSignalGenerations(t *testing.T) {
     52 	var m Mutex
     53 	c := NewCond(&m)
     54 	n := 100
     55 	running := make(chan bool, n)
     56 	awake := make(chan int, n)
     57 	for i := 0; i < n; i++ {
     58 		go func(i int) {
     59 			m.Lock()
     60 			running <- true
     61 			c.Wait()
     62 			awake <- i
     63 			m.Unlock()
     64 		}(i)
     65 		if i > 0 {
     66 			a := <-awake
     67 			if a != i-1 {
     68 				t.Fatalf("wrong goroutine woke up: want %d, got %d", i-1, a)
     69 			}
     70 		}
     71 		<-running
     72 		m.Lock()
     73 		c.Signal()
     74 		m.Unlock()
     75 	}
     76 }
     77 
     78 func TestCondBroadcast(t *testing.T) {
     79 	var m Mutex
     80 	c := NewCond(&m)
     81 	n := 200
     82 	running := make(chan int, n)
     83 	awake := make(chan int, n)
     84 	exit := false
     85 	for i := 0; i < n; i++ {
     86 		go func(g int) {
     87 			m.Lock()
     88 			for !exit {
     89 				running <- g
     90 				c.Wait()
     91 				awake <- g
     92 			}
     93 			m.Unlock()
     94 		}(i)
     95 	}
     96 	for i := 0; i < n; i++ {
     97 		for i := 0; i < n; i++ {
     98 			<-running // Will deadlock unless n are running.
     99 		}
    100 		if i == n-1 {
    101 			m.Lock()
    102 			exit = true
    103 			m.Unlock()
    104 		}
    105 		select {
    106 		case <-awake:
    107 			t.Fatal("goroutine not asleep")
    108 		default:
    109 		}
    110 		m.Lock()
    111 		c.Broadcast()
    112 		m.Unlock()
    113 		seen := make([]bool, n)
    114 		for i := 0; i < n; i++ {
    115 			g := <-awake
    116 			if seen[g] {
    117 				t.Fatal("goroutine woke up twice")
    118 			}
    119 			seen[g] = true
    120 		}
    121 	}
    122 	select {
    123 	case <-running:
    124 		t.Fatal("goroutine did not exit")
    125 	default:
    126 	}
    127 	c.Broadcast()
    128 }
    129 
    130 func TestRace(t *testing.T) {
    131 	x := 0
    132 	c := NewCond(&Mutex{})
    133 	done := make(chan bool)
    134 	go func() {
    135 		c.L.Lock()
    136 		x = 1
    137 		c.Wait()
    138 		if x != 2 {
    139 			t.Fatal("want 2")
    140 		}
    141 		x = 3
    142 		c.Signal()
    143 		c.L.Unlock()
    144 		done <- true
    145 	}()
    146 	go func() {
    147 		c.L.Lock()
    148 		for {
    149 			if x == 1 {
    150 				x = 2
    151 				c.Signal()
    152 				break
    153 			}
    154 			c.L.Unlock()
    155 			runtime.Gosched()
    156 			c.L.Lock()
    157 		}
    158 		c.L.Unlock()
    159 		done <- true
    160 	}()
    161 	go func() {
    162 		c.L.Lock()
    163 		for {
    164 			if x == 2 {
    165 				c.Wait()
    166 				if x != 3 {
    167 					t.Fatal("want 3")
    168 				}
    169 				break
    170 			}
    171 			if x == 3 {
    172 				break
    173 			}
    174 			c.L.Unlock()
    175 			runtime.Gosched()
    176 			c.L.Lock()
    177 		}
    178 		c.L.Unlock()
    179 		done <- true
    180 	}()
    181 	<-done
    182 	<-done
    183 	<-done
    184 }
    185 
    186 func TestCondCopy(t *testing.T) {
    187 	defer func() {
    188 		err := recover()
    189 		if err == nil || err.(string) != "sync.Cond is copied" {
    190 			t.Fatalf("got %v, expect sync.Cond is copied", err)
    191 		}
    192 	}()
    193 	c := Cond{L: &Mutex{}}
    194 	c.Signal()
    195 	c2 := c
    196 	c2.Signal()
    197 }
    198 
    199 func BenchmarkCond1(b *testing.B) {
    200 	benchmarkCond(b, 1)
    201 }
    202 
    203 func BenchmarkCond2(b *testing.B) {
    204 	benchmarkCond(b, 2)
    205 }
    206 
    207 func BenchmarkCond4(b *testing.B) {
    208 	benchmarkCond(b, 4)
    209 }
    210 
    211 func BenchmarkCond8(b *testing.B) {
    212 	benchmarkCond(b, 8)
    213 }
    214 
    215 func BenchmarkCond16(b *testing.B) {
    216 	benchmarkCond(b, 16)
    217 }
    218 
    219 func BenchmarkCond32(b *testing.B) {
    220 	benchmarkCond(b, 32)
    221 }
    222 
    223 func benchmarkCond(b *testing.B, waiters int) {
    224 	c := NewCond(&Mutex{})
    225 	done := make(chan bool)
    226 	id := 0
    227 
    228 	for routine := 0; routine < waiters+1; routine++ {
    229 		go func() {
    230 			for i := 0; i < b.N; i++ {
    231 				c.L.Lock()
    232 				if id == -1 {
    233 					c.L.Unlock()
    234 					break
    235 				}
    236 				id++
    237 				if id == waiters+1 {
    238 					id = 0
    239 					c.Broadcast()
    240 				} else {
    241 					c.Wait()
    242 				}
    243 				c.L.Unlock()
    244 			}
    245 			c.L.Lock()
    246 			id = -1
    247 			c.Broadcast()
    248 			c.L.Unlock()
    249 			done <- true
    250 		}()
    251 	}
    252 	for routine := 0; routine < waiters+1; routine++ {
    253 		<-done
    254 	}
    255 }
    256