Source file src/sync/waitgroup_test.go

     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  	. "sync"
     9  	"sync/atomic"
    10  	"testing"
    11  )
    12  
    13  func testWaitGroup(t *testing.T, wg1 *WaitGroup, wg2 *WaitGroup) {
    14  	n := 16
    15  	wg1.Add(n)
    16  	wg2.Add(n)
    17  	exited := make(chan bool, n)
    18  	for i := 0; i != n; i++ {
    19  		go func() {
    20  			wg1.Done()
    21  			wg2.Wait()
    22  			exited <- true
    23  		}()
    24  	}
    25  	wg1.Wait()
    26  	for i := 0; i != n; i++ {
    27  		select {
    28  		case <-exited:
    29  			t.Fatal("WaitGroup released group too soon")
    30  		default:
    31  		}
    32  		wg2.Done()
    33  	}
    34  	for i := 0; i != n; i++ {
    35  		<-exited // Will block if barrier fails to unlock someone.
    36  	}
    37  }
    38  
    39  func TestWaitGroup(t *testing.T) {
    40  	wg1 := &WaitGroup{}
    41  	wg2 := &WaitGroup{}
    42  
    43  	// Run the same test a few times to ensure barrier is in a proper state.
    44  	for i := 0; i != 8; i++ {
    45  		testWaitGroup(t, wg1, wg2)
    46  	}
    47  }
    48  
    49  func TestWaitGroupMisuse(t *testing.T) {
    50  	defer func() {
    51  		err := recover()
    52  		if err != "sync: negative WaitGroup counter" {
    53  			t.Fatalf("Unexpected panic: %#v", err)
    54  		}
    55  	}()
    56  	wg := &WaitGroup{}
    57  	wg.Add(1)
    58  	wg.Done()
    59  	wg.Done()
    60  	t.Fatal("Should panic")
    61  }
    62  
    63  func TestWaitGroupRace(t *testing.T) {
    64  	// Run this test for about 1ms.
    65  	for i := 0; i < 1000; i++ {
    66  		wg := &WaitGroup{}
    67  		n := new(int32)
    68  		// spawn goroutine 1
    69  		wg.Add(1)
    70  		go func() {
    71  			atomic.AddInt32(n, 1)
    72  			wg.Done()
    73  		}()
    74  		// spawn goroutine 2
    75  		wg.Add(1)
    76  		go func() {
    77  			atomic.AddInt32(n, 1)
    78  			wg.Done()
    79  		}()
    80  		// Wait for goroutine 1 and 2
    81  		wg.Wait()
    82  		if atomic.LoadInt32(n) != 2 {
    83  			t.Fatal("Spurious wakeup from Wait")
    84  		}
    85  	}
    86  }
    87  
    88  func TestWaitGroupAlign(t *testing.T) {
    89  	type X struct {
    90  		x  byte
    91  		wg WaitGroup
    92  	}
    93  	var x X
    94  	x.wg.Add(1)
    95  	go func(x *X) {
    96  		x.wg.Done()
    97  	}(&x)
    98  	x.wg.Wait()
    99  }
   100  
   101  func BenchmarkWaitGroupUncontended(b *testing.B) {
   102  	type PaddedWaitGroup struct {
   103  		WaitGroup
   104  		pad [128]uint8
   105  	}
   106  	b.RunParallel(func(pb *testing.PB) {
   107  		var wg PaddedWaitGroup
   108  		for pb.Next() {
   109  			wg.Add(1)
   110  			wg.Done()
   111  			wg.Wait()
   112  		}
   113  	})
   114  }
   115  
   116  func benchmarkWaitGroupAddDone(b *testing.B, localWork int) {
   117  	var wg WaitGroup
   118  	b.RunParallel(func(pb *testing.PB) {
   119  		foo := 0
   120  		for pb.Next() {
   121  			wg.Add(1)
   122  			for i := 0; i < localWork; i++ {
   123  				foo *= 2
   124  				foo /= 2
   125  			}
   126  			wg.Done()
   127  		}
   128  		_ = foo
   129  	})
   130  }
   131  
   132  func BenchmarkWaitGroupAddDone(b *testing.B) {
   133  	benchmarkWaitGroupAddDone(b, 0)
   134  }
   135  
   136  func BenchmarkWaitGroupAddDoneWork(b *testing.B) {
   137  	benchmarkWaitGroupAddDone(b, 100)
   138  }
   139  
   140  func benchmarkWaitGroupWait(b *testing.B, localWork int) {
   141  	var wg WaitGroup
   142  	b.RunParallel(func(pb *testing.PB) {
   143  		foo := 0
   144  		for pb.Next() {
   145  			wg.Wait()
   146  			for i := 0; i < localWork; i++ {
   147  				foo *= 2
   148  				foo /= 2
   149  			}
   150  		}
   151  		_ = foo
   152  	})
   153  }
   154  
   155  func BenchmarkWaitGroupWait(b *testing.B) {
   156  	benchmarkWaitGroupWait(b, 0)
   157  }
   158  
   159  func BenchmarkWaitGroupWaitWork(b *testing.B) {
   160  	benchmarkWaitGroupWait(b, 100)
   161  }
   162  
   163  func BenchmarkWaitGroupActuallyWait(b *testing.B) {
   164  	b.ReportAllocs()
   165  	b.RunParallel(func(pb *testing.PB) {
   166  		for pb.Next() {
   167  			var wg WaitGroup
   168  			wg.Add(1)
   169  			go func() {
   170  				wg.Done()
   171  			}()
   172  			wg.Wait()
   173  		}
   174  	})
   175  }
   176  

View as plain text