github.com/sandwich-go/boost@v1.3.29/xsync/cond_test.go (about)

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