github.com/likebike/go--@v0.0.0-20190911215757-0bd925d16e96/go/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() {
    22  			wg1.Done()
    23  			wg2.Wait()
    24  			exited <- true
    25  		}()
    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 runtime.NumCPU() <= 4 {
    74  		t.Skip("NumCPU<=4, skipping: this test requires parallelism")
    75  	}
    76  	defer func() {
    77  		err := recover()
    78  		if err != "sync: negative WaitGroup counter" &&
    79  			err != "sync: WaitGroup misuse: Add called concurrently with Wait" &&
    80  			err != "sync: WaitGroup is reused before previous Wait has returned" {
    81  			t.Fatalf("Unexpected panic: %#v", err)
    82  		}
    83  	}()
    84  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
    85  	done := make(chan interface{}, 2)
    86  	// The detection is opportunistic, so we want it to panic
    87  	// at least in one run out of a million.
    88  	for i := 0; i < 1e6; i++ {
    89  		var wg WaitGroup
    90  		var here uint32
    91  		wg.Add(1)
    92  		go func() {
    93  			defer func() {
    94  				done <- recover()
    95  			}()
    96  			atomic.AddUint32(&here, 1)
    97  			for atomic.LoadUint32(&here) != 3 {
    98  				// spin
    99  			}
   100  			wg.Wait()
   101  		}()
   102  		go func() {
   103  			defer func() {
   104  				done <- recover()
   105  			}()
   106  			atomic.AddUint32(&here, 1)
   107  			for atomic.LoadUint32(&here) != 3 {
   108  				// spin
   109  			}
   110  			wg.Add(1) // This is the bad guy.
   111  			wg.Done()
   112  		}()
   113  		atomic.AddUint32(&here, 1)
   114  		for atomic.LoadUint32(&here) != 3 {
   115  			// spin
   116  		}
   117  		wg.Done()
   118  		for j := 0; j < 2; j++ {
   119  			if err := <-done; err != nil {
   120  				panic(err)
   121  			}
   122  		}
   123  	}
   124  	t.Fatal("Should panic")
   125  }
   126  
   127  func TestWaitGroupMisuse3(t *testing.T) {
   128  	knownRacy(t)
   129  	if runtime.NumCPU() <= 1 {
   130  		t.Skip("NumCPU==1, skipping: this test requires parallelism")
   131  	}
   132  	defer func() {
   133  		err := recover()
   134  		if err != "sync: negative WaitGroup counter" &&
   135  			err != "sync: WaitGroup misuse: Add called concurrently with Wait" &&
   136  			err != "sync: WaitGroup is reused before previous Wait has returned" {
   137  			t.Fatalf("Unexpected panic: %#v", err)
   138  		}
   139  	}()
   140  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
   141  	done := make(chan interface{}, 2)
   142  	// The detection is opportunistically, so we want it to panic
   143  	// at least in one run out of a million.
   144  	for i := 0; i < 1e6; i++ {
   145  		var wg WaitGroup
   146  		wg.Add(1)
   147  		go func() {
   148  			defer func() {
   149  				done <- recover()
   150  			}()
   151  			wg.Done()
   152  		}()
   153  		go func() {
   154  			defer func() {
   155  				done <- recover()
   156  			}()
   157  			wg.Wait()
   158  			// Start reusing the wg before waiting for the Wait below to return.
   159  			wg.Add(1)
   160  			go func() {
   161  				wg.Done()
   162  			}()
   163  			wg.Wait()
   164  		}()
   165  		wg.Wait()
   166  		for j := 0; j < 2; j++ {
   167  			if err := <-done; err != nil {
   168  				panic(err)
   169  			}
   170  		}
   171  	}
   172  	t.Fatal("Should panic")
   173  }
   174  
   175  func TestWaitGroupRace(t *testing.T) {
   176  	// Run this test for about 1ms.
   177  	for i := 0; i < 1000; i++ {
   178  		wg := &WaitGroup{}
   179  		n := new(int32)
   180  		// spawn goroutine 1
   181  		wg.Add(1)
   182  		go func() {
   183  			atomic.AddInt32(n, 1)
   184  			wg.Done()
   185  		}()
   186  		// spawn goroutine 2
   187  		wg.Add(1)
   188  		go func() {
   189  			atomic.AddInt32(n, 1)
   190  			wg.Done()
   191  		}()
   192  		// Wait for goroutine 1 and 2
   193  		wg.Wait()
   194  		if atomic.LoadInt32(n) != 2 {
   195  			t.Fatal("Spurious wakeup from Wait")
   196  		}
   197  	}
   198  }
   199  
   200  func TestWaitGroupAlign(t *testing.T) {
   201  	type X struct {
   202  		x  byte
   203  		wg WaitGroup
   204  	}
   205  	var x X
   206  	x.wg.Add(1)
   207  	go func(x *X) {
   208  		x.wg.Done()
   209  	}(&x)
   210  	x.wg.Wait()
   211  }
   212  
   213  func BenchmarkWaitGroupUncontended(b *testing.B) {
   214  	type PaddedWaitGroup struct {
   215  		WaitGroup
   216  		pad [128]uint8
   217  	}
   218  	b.RunParallel(func(pb *testing.PB) {
   219  		var wg PaddedWaitGroup
   220  		for pb.Next() {
   221  			wg.Add(1)
   222  			wg.Done()
   223  			wg.Wait()
   224  		}
   225  	})
   226  }
   227  
   228  func benchmarkWaitGroupAddDone(b *testing.B, localWork int) {
   229  	var wg WaitGroup
   230  	b.RunParallel(func(pb *testing.PB) {
   231  		foo := 0
   232  		for pb.Next() {
   233  			wg.Add(1)
   234  			for i := 0; i < localWork; i++ {
   235  				foo *= 2
   236  				foo /= 2
   237  			}
   238  			wg.Done()
   239  		}
   240  		_ = foo
   241  	})
   242  }
   243  
   244  func BenchmarkWaitGroupAddDone(b *testing.B) {
   245  	benchmarkWaitGroupAddDone(b, 0)
   246  }
   247  
   248  func BenchmarkWaitGroupAddDoneWork(b *testing.B) {
   249  	benchmarkWaitGroupAddDone(b, 100)
   250  }
   251  
   252  func benchmarkWaitGroupWait(b *testing.B, localWork int) {
   253  	var wg WaitGroup
   254  	b.RunParallel(func(pb *testing.PB) {
   255  		foo := 0
   256  		for pb.Next() {
   257  			wg.Wait()
   258  			for i := 0; i < localWork; i++ {
   259  				foo *= 2
   260  				foo /= 2
   261  			}
   262  		}
   263  		_ = foo
   264  	})
   265  }
   266  
   267  func BenchmarkWaitGroupWait(b *testing.B) {
   268  	benchmarkWaitGroupWait(b, 0)
   269  }
   270  
   271  func BenchmarkWaitGroupWaitWork(b *testing.B) {
   272  	benchmarkWaitGroupWait(b, 100)
   273  }
   274  
   275  func BenchmarkWaitGroupActuallyWait(b *testing.B) {
   276  	b.ReportAllocs()
   277  	b.RunParallel(func(pb *testing.PB) {
   278  		for pb.Next() {
   279  			var wg WaitGroup
   280  			wg.Add(1)
   281  			go func() {
   282  				wg.Done()
   283  			}()
   284  			wg.Wait()
   285  		}
   286  	})
   287  }