github.com/letsencrypt/go@v0.0.0-20160714163537-4054769a31f6/src/sync/waitgroup_test.go (about)

     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  	"internal/race"
     9  	"runtime"
    10  	. "sync"
    11  	"sync/atomic"
    12  	"testing"
    13  )
    14  
    15  func testWaitGroup(t *testing.T, wg1 *WaitGroup, wg2 *WaitGroup) {
    16  	n := 16
    17  	wg1.Add(n)
    18  	wg2.Add(n)
    19  	exited := make(chan bool, n)
    20  	for i := 0; i != n; i++ {
    21  		go func(i int) {
    22  			wg1.Done()
    23  			wg2.Wait()
    24  			exited <- true
    25  		}(i)
    26  	}
    27  	wg1.Wait()
    28  	for i := 0; i != n; i++ {
    29  		select {
    30  		case <-exited:
    31  			t.Fatal("WaitGroup released group too soon")
    32  		default:
    33  		}
    34  		wg2.Done()
    35  	}
    36  	for i := 0; i != n; i++ {
    37  		<-exited // Will block if barrier fails to unlock someone.
    38  	}
    39  }
    40  
    41  func TestWaitGroup(t *testing.T) {
    42  	wg1 := &WaitGroup{}
    43  	wg2 := &WaitGroup{}
    44  
    45  	// Run the same test a few times to ensure barrier is in a proper state.
    46  	for i := 0; i != 8; i++ {
    47  		testWaitGroup(t, wg1, wg2)
    48  	}
    49  }
    50  
    51  func knownRacy(t *testing.T) {
    52  	if race.Enabled {
    53  		t.Skip("skipping known-racy test under the race detector")
    54  	}
    55  }
    56  
    57  func TestWaitGroupMisuse(t *testing.T) {
    58  	defer func() {
    59  		err := recover()
    60  		if err != "sync: negative WaitGroup counter" {
    61  			t.Fatalf("Unexpected panic: %#v", err)
    62  		}
    63  	}()
    64  	wg := &WaitGroup{}
    65  	wg.Add(1)
    66  	wg.Done()
    67  	wg.Done()
    68  	t.Fatal("Should panic")
    69  }
    70  
    71  func TestWaitGroupMisuse2(t *testing.T) {
    72  	knownRacy(t)
    73  	if testing.Short() {
    74  		t.Skip("skipping flaky test in short mode; see issue 11443")
    75  	}
    76  	if runtime.NumCPU() <= 2 {
    77  		t.Skip("NumCPU<=2, skipping: this test requires parallelism")
    78  	}
    79  	defer func() {
    80  		err := recover()
    81  		if err != "sync: negative WaitGroup counter" &&
    82  			err != "sync: WaitGroup misuse: Add called concurrently with Wait" &&
    83  			err != "sync: WaitGroup is reused before previous Wait has returned" {
    84  			t.Fatalf("Unexpected panic: %#v", err)
    85  		}
    86  	}()
    87  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
    88  	done := make(chan interface{}, 2)
    89  	// The detection is opportunistically, so we want it to panic
    90  	// at least in one run out of a million.
    91  	for i := 0; i < 1e6; i++ {
    92  		var wg WaitGroup
    93  		wg.Add(1)
    94  		go func() {
    95  			defer func() {
    96  				done <- recover()
    97  			}()
    98  			wg.Wait()
    99  		}()
   100  		go func() {
   101  			defer func() {
   102  				done <- recover()
   103  			}()
   104  			wg.Add(1) // This is the bad guy.
   105  			wg.Done()
   106  		}()
   107  		wg.Done()
   108  		for j := 0; j < 2; j++ {
   109  			if err := <-done; err != nil {
   110  				panic(err)
   111  			}
   112  		}
   113  	}
   114  	t.Fatal("Should panic")
   115  }
   116  
   117  func TestWaitGroupMisuse3(t *testing.T) {
   118  	knownRacy(t)
   119  	if runtime.NumCPU() <= 1 {
   120  		t.Skip("NumCPU==1, skipping: this test requires parallelism")
   121  	}
   122  	defer func() {
   123  		err := recover()
   124  		if err != "sync: negative WaitGroup counter" &&
   125  			err != "sync: WaitGroup misuse: Add called concurrently with Wait" &&
   126  			err != "sync: WaitGroup is reused before previous Wait has returned" {
   127  			t.Fatalf("Unexpected panic: %#v", err)
   128  		}
   129  	}()
   130  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
   131  	done := make(chan interface{}, 2)
   132  	// The detection is opportunistically, so we want it to panic
   133  	// at least in one run out of a million.
   134  	for i := 0; i < 1e6; i++ {
   135  		var wg WaitGroup
   136  		wg.Add(1)
   137  		go func() {
   138  			defer func() {
   139  				done <- recover()
   140  			}()
   141  			wg.Done()
   142  		}()
   143  		go func() {
   144  			defer func() {
   145  				done <- recover()
   146  			}()
   147  			wg.Wait()
   148  			// Start reusing the wg before waiting for the Wait below to return.
   149  			wg.Add(1)
   150  			go func() {
   151  				wg.Done()
   152  			}()
   153  			wg.Wait()
   154  		}()
   155  		wg.Wait()
   156  		for j := 0; j < 2; j++ {
   157  			if err := <-done; err != nil {
   158  				panic(err)
   159  			}
   160  		}
   161  	}
   162  	t.Fatal("Should panic")
   163  }
   164  
   165  func TestWaitGroupRace(t *testing.T) {
   166  	// Run this test for about 1ms.
   167  	for i := 0; i < 1000; i++ {
   168  		wg := &WaitGroup{}
   169  		n := new(int32)
   170  		// spawn goroutine 1
   171  		wg.Add(1)
   172  		go func() {
   173  			atomic.AddInt32(n, 1)
   174  			wg.Done()
   175  		}()
   176  		// spawn goroutine 2
   177  		wg.Add(1)
   178  		go func() {
   179  			atomic.AddInt32(n, 1)
   180  			wg.Done()
   181  		}()
   182  		// Wait for goroutine 1 and 2
   183  		wg.Wait()
   184  		if atomic.LoadInt32(n) != 2 {
   185  			t.Fatal("Spurious wakeup from Wait")
   186  		}
   187  	}
   188  }
   189  
   190  func TestWaitGroupAlign(t *testing.T) {
   191  	type X struct {
   192  		x  byte
   193  		wg WaitGroup
   194  	}
   195  	var x X
   196  	x.wg.Add(1)
   197  	go func(x *X) {
   198  		x.wg.Done()
   199  	}(&x)
   200  	x.wg.Wait()
   201  }
   202  
   203  func BenchmarkWaitGroupUncontended(b *testing.B) {
   204  	type PaddedWaitGroup struct {
   205  		WaitGroup
   206  		pad [128]uint8
   207  	}
   208  	b.RunParallel(func(pb *testing.PB) {
   209  		var wg PaddedWaitGroup
   210  		for pb.Next() {
   211  			wg.Add(1)
   212  			wg.Done()
   213  			wg.Wait()
   214  		}
   215  	})
   216  }
   217  
   218  func benchmarkWaitGroupAddDone(b *testing.B, localWork int) {
   219  	var wg WaitGroup
   220  	b.RunParallel(func(pb *testing.PB) {
   221  		foo := 0
   222  		for pb.Next() {
   223  			wg.Add(1)
   224  			for i := 0; i < localWork; i++ {
   225  				foo *= 2
   226  				foo /= 2
   227  			}
   228  			wg.Done()
   229  		}
   230  		_ = foo
   231  	})
   232  }
   233  
   234  func BenchmarkWaitGroupAddDone(b *testing.B) {
   235  	benchmarkWaitGroupAddDone(b, 0)
   236  }
   237  
   238  func BenchmarkWaitGroupAddDoneWork(b *testing.B) {
   239  	benchmarkWaitGroupAddDone(b, 100)
   240  }
   241  
   242  func benchmarkWaitGroupWait(b *testing.B, localWork int) {
   243  	var wg WaitGroup
   244  	b.RunParallel(func(pb *testing.PB) {
   245  		foo := 0
   246  		for pb.Next() {
   247  			wg.Wait()
   248  			for i := 0; i < localWork; i++ {
   249  				foo *= 2
   250  				foo /= 2
   251  			}
   252  		}
   253  		_ = foo
   254  	})
   255  }
   256  
   257  func BenchmarkWaitGroupWait(b *testing.B) {
   258  	benchmarkWaitGroupWait(b, 0)
   259  }
   260  
   261  func BenchmarkWaitGroupWaitWork(b *testing.B) {
   262  	benchmarkWaitGroupWait(b, 100)
   263  }
   264  
   265  func BenchmarkWaitGroupActuallyWait(b *testing.B) {
   266  	b.ReportAllocs()
   267  	b.RunParallel(func(pb *testing.PB) {
   268  		for pb.Next() {
   269  			var wg WaitGroup
   270  			wg.Add(1)
   271  			go func() {
   272  				wg.Done()
   273  			}()
   274  			wg.Wait()
   275  		}
   276  	})
   277  }