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