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  	"bytes"
     9  	"internal/testenv"
    10  	"os"
    11  	"os/exec"
    12  	"strings"
    13  	. "sync"
    14  	"sync/atomic"
    15  	"testing"
    16  )
    17  
    18  func testWaitGroup(t *testing.T, wg1 *WaitGroup, wg2 *WaitGroup) {
    19  	n := 16
    20  	wg1.Add(n)
    21  	wg2.Add(n)
    22  	exited := make(chan bool, n)
    23  	for i := 0; i != n; i++ {
    24  		go func() {
    25  			wg1.Done()
    26  			wg2.Wait()
    27  			exited <- true
    28  		}()
    29  	}
    30  	wg1.Wait()
    31  	for i := 0; i != n; i++ {
    32  		select {
    33  		case <-exited:
    34  			t.Fatal("WaitGroup released group too soon")
    35  		default:
    36  		}
    37  		wg2.Done()
    38  	}
    39  	for i := 0; i != n; i++ {
    40  		<-exited // Will block if barrier fails to unlock someone.
    41  	}
    42  }
    43  
    44  func TestWaitGroup(t *testing.T) {
    45  	wg1 := &WaitGroup{}
    46  	wg2 := &WaitGroup{}
    47  
    48  	// Run the same test a few times to ensure barrier is in a proper state.
    49  	for i := 0; i != 8; i++ {
    50  		testWaitGroup(t, wg1, wg2)
    51  	}
    52  }
    53  
    54  func TestWaitGroupMisuse(t *testing.T) {
    55  	defer func() {
    56  		err := recover()
    57  		if err != "sync: negative WaitGroup counter" {
    58  			t.Fatalf("Unexpected panic: %#v", err)
    59  		}
    60  	}()
    61  	wg := &WaitGroup{}
    62  	wg.Add(1)
    63  	wg.Done()
    64  	wg.Done()
    65  	t.Fatal("Should panic")
    66  }
    67  
    68  func TestWaitGroupRace(t *testing.T) {
    69  	// Run this test for about 1ms.
    70  	for i := 0; i < 1000; i++ {
    71  		wg := &WaitGroup{}
    72  		n := new(int32)
    73  		// spawn goroutine 1
    74  		wg.Add(1)
    75  		go func() {
    76  			atomic.AddInt32(n, 1)
    77  			wg.Done()
    78  		}()
    79  		// spawn goroutine 2
    80  		wg.Add(1)
    81  		go func() {
    82  			atomic.AddInt32(n, 1)
    83  			wg.Done()
    84  		}()
    85  		// Wait for goroutine 1 and 2
    86  		wg.Wait()
    87  		if atomic.LoadInt32(n) != 2 {
    88  			t.Fatal("Spurious wakeup from Wait")
    89  		}
    90  	}
    91  }
    92  
    93  func TestWaitGroupAlign(t *testing.T) {
    94  	type X struct {
    95  		x  byte
    96  		wg WaitGroup
    97  	}
    98  	var x X
    99  	x.wg.Add(1)
   100  	go func(x *X) {
   101  		x.wg.Done()
   102  	}(&x)
   103  	x.wg.Wait()
   104  }
   105  
   106  func TestWaitGroupGo(t *testing.T) {
   107  	wg := &WaitGroup{}
   108  	var i int
   109  	wg.Go(func() {
   110  		i++
   111  	})
   112  	wg.Wait()
   113  	if i != 1 {
   114  		t.Fatalf("got %d, want 1", i)
   115  	}
   116  }
   117  
   118  // This test ensures that an unhandled panic in a Go goroutine terminates
   119  // the process without causing Wait to unblock; previously there was a race.
   120  func TestIssue76126(t *testing.T) {
   121  	testenv.MustHaveExec(t)
   122  	if os.Getenv("SYNC_TEST_CHILD") != "1" {
   123  		// Call child in a child process
   124  		// and inspect its failure message.
   125  		cmd := exec.Command(os.Args[0], "-test.run=^TestIssue76126$")
   126  		cmd.Env = append(os.Environ(), "SYNC_TEST_CHILD=1")
   127  		buf := new(bytes.Buffer)
   128  		cmd.Stderr = buf
   129  		cmd.Run() // ignore error
   130  		got := buf.String()
   131  		if !strings.Contains(got, "panic: test") {
   132  			t.Errorf("missing panic: test\n%s", got)
   133  		}
   134  		return
   135  	}
   136  	var wg WaitGroup
   137  	wg.Go(func() {
   138  		panic("test")
   139  	})
   140  	wg.Wait()              // process should terminate here
   141  	panic("Wait returned") // must not be reached
   142  }
   143  
   144  func BenchmarkWaitGroupUncontended(b *testing.B) {
   145  	type PaddedWaitGroup struct {
   146  		WaitGroup
   147  		pad [128]uint8
   148  	}
   149  	b.RunParallel(func(pb *testing.PB) {
   150  		var wg PaddedWaitGroup
   151  		for pb.Next() {
   152  			wg.Add(1)
   153  			wg.Done()
   154  			wg.Wait()
   155  		}
   156  	})
   157  }
   158  
   159  func benchmarkWaitGroupAddDone(b *testing.B, localWork int) {
   160  	var wg WaitGroup
   161  	b.RunParallel(func(pb *testing.PB) {
   162  		foo := 0
   163  		for pb.Next() {
   164  			wg.Add(1)
   165  			for i := 0; i < localWork; i++ {
   166  				foo *= 2
   167  				foo /= 2
   168  			}
   169  			wg.Done()
   170  		}
   171  		_ = foo
   172  	})
   173  }
   174  
   175  func BenchmarkWaitGroupAddDone(b *testing.B) {
   176  	benchmarkWaitGroupAddDone(b, 0)
   177  }
   178  
   179  func BenchmarkWaitGroupAddDoneWork(b *testing.B) {
   180  	benchmarkWaitGroupAddDone(b, 100)
   181  }
   182  
   183  func benchmarkWaitGroupWait(b *testing.B, localWork int) {
   184  	var wg WaitGroup
   185  	b.RunParallel(func(pb *testing.PB) {
   186  		foo := 0
   187  		for pb.Next() {
   188  			wg.Wait()
   189  			for i := 0; i < localWork; i++ {
   190  				foo *= 2
   191  				foo /= 2
   192  			}
   193  		}
   194  		_ = foo
   195  	})
   196  }
   197  
   198  func BenchmarkWaitGroupWait(b *testing.B) {
   199  	benchmarkWaitGroupWait(b, 0)
   200  }
   201  
   202  func BenchmarkWaitGroupWaitWork(b *testing.B) {
   203  	benchmarkWaitGroupWait(b, 100)
   204  }
   205  
   206  func BenchmarkWaitGroupActuallyWait(b *testing.B) {
   207  	b.ReportAllocs()
   208  	b.RunParallel(func(pb *testing.PB) {
   209  		for pb.Next() {
   210  			var wg WaitGroup
   211  			wg.Add(1)
   212  			go func() {
   213  				wg.Done()
   214  			}()
   215  			wg.Wait()
   216  		}
   217  	})
   218  }
   219  

View as plain text