...
Run Format

Source file src/sync/waitgroup_test.go

Documentation: sync

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

View as plain text