...
Run Format

Source file src/runtime/race/testdata/waitgroup_test.go

Documentation: runtime/race/testdata

  // Copyright 2012 The Go Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style
  // license that can be found in the LICENSE file.
  
  package race_test
  
  import (
  	"runtime"
  	"sync"
  	"testing"
  	"time"
  )
  
  func TestNoRaceWaitGroup(t *testing.T) {
  	var x int
  	var wg sync.WaitGroup
  	n := 1
  	for i := 0; i < n; i++ {
  		wg.Add(1)
  		j := i
  		go func() {
  			x = j
  			wg.Done()
  		}()
  	}
  	wg.Wait()
  }
  
  func TestRaceWaitGroup(t *testing.T) {
  	var x int
  	var wg sync.WaitGroup
  	n := 2
  	for i := 0; i < n; i++ {
  		wg.Add(1)
  		j := i
  		go func() {
  			x = j
  			wg.Done()
  		}()
  	}
  	wg.Wait()
  }
  
  func TestNoRaceWaitGroup2(t *testing.T) {
  	var x int
  	var wg sync.WaitGroup
  	wg.Add(1)
  	go func() {
  		x = 1
  		wg.Done()
  	}()
  	wg.Wait()
  	x = 2
  }
  
  // incrementing counter in Add and locking wg's mutex
  func TestRaceWaitGroupAsMutex(t *testing.T) {
  	var x int
  	var wg sync.WaitGroup
  	c := make(chan bool, 2)
  	go func() {
  		wg.Wait()
  		time.Sleep(100 * time.Millisecond)
  		wg.Add(+1)
  		x = 1
  		wg.Add(-1)
  		c <- true
  	}()
  	go func() {
  		wg.Wait()
  		time.Sleep(100 * time.Millisecond)
  		wg.Add(+1)
  		x = 2
  		wg.Add(-1)
  		c <- true
  	}()
  	<-c
  	<-c
  }
  
  // Incorrect usage: Add is too late.
  func TestRaceWaitGroupWrongWait(t *testing.T) {
  	c := make(chan bool, 2)
  	var x int
  	var wg sync.WaitGroup
  	go func() {
  		wg.Add(1)
  		runtime.Gosched()
  		x = 1
  		wg.Done()
  		c <- true
  	}()
  	go func() {
  		wg.Add(1)
  		runtime.Gosched()
  		x = 2
  		wg.Done()
  		c <- true
  	}()
  	wg.Wait()
  	<-c
  	<-c
  }
  
  func TestRaceWaitGroupWrongAdd(t *testing.T) {
  	c := make(chan bool, 2)
  	var wg sync.WaitGroup
  	go func() {
  		wg.Add(1)
  		time.Sleep(100 * time.Millisecond)
  		wg.Done()
  		c <- true
  	}()
  	go func() {
  		wg.Add(1)
  		time.Sleep(100 * time.Millisecond)
  		wg.Done()
  		c <- true
  	}()
  	time.Sleep(50 * time.Millisecond)
  	wg.Wait()
  	<-c
  	<-c
  }
  
  func TestNoRaceWaitGroupMultipleWait(t *testing.T) {
  	c := make(chan bool, 2)
  	var wg sync.WaitGroup
  	go func() {
  		wg.Wait()
  		c <- true
  	}()
  	go func() {
  		wg.Wait()
  		c <- true
  	}()
  	wg.Wait()
  	<-c
  	<-c
  }
  
  func TestNoRaceWaitGroupMultipleWait2(t *testing.T) {
  	c := make(chan bool, 2)
  	var wg sync.WaitGroup
  	wg.Add(2)
  	go func() {
  		wg.Done()
  		wg.Wait()
  		c <- true
  	}()
  	go func() {
  		wg.Done()
  		wg.Wait()
  		c <- true
  	}()
  	wg.Wait()
  	<-c
  	<-c
  }
  
  func TestNoRaceWaitGroupMultipleWait3(t *testing.T) {
  	const P = 3
  	var data [P]int
  	done := make(chan bool, P)
  	var wg sync.WaitGroup
  	wg.Add(P)
  	for p := 0; p < P; p++ {
  		go func(p int) {
  			data[p] = 42
  			wg.Done()
  		}(p)
  	}
  	for p := 0; p < P; p++ {
  		go func() {
  			wg.Wait()
  			for p1 := 0; p1 < P; p1++ {
  				_ = data[p1]
  			}
  			done <- true
  		}()
  	}
  	for p := 0; p < P; p++ {
  		<-done
  	}
  }
  
  // Correct usage but still a race
  func TestRaceWaitGroup2(t *testing.T) {
  	var x int
  	var wg sync.WaitGroup
  	wg.Add(2)
  	go func() {
  		x = 1
  		wg.Done()
  	}()
  	go func() {
  		x = 2
  		wg.Done()
  	}()
  	wg.Wait()
  }
  
  func TestNoRaceWaitGroupPanicRecover(t *testing.T) {
  	var x int
  	var wg sync.WaitGroup
  	defer func() {
  		err := recover()
  		if err != "sync: negative WaitGroup counter" {
  			t.Fatalf("Unexpected panic: %#v", err)
  		}
  		x = 2
  	}()
  	x = 1
  	wg.Add(-1)
  }
  
  // TODO: this is actually a panic-synchronization test, not a
  // WaitGroup test. Move it to another *_test file
  // Is it possible to get a race by synchronization via panic?
  func TestNoRaceWaitGroupPanicRecover2(t *testing.T) {
  	var x int
  	var wg sync.WaitGroup
  	ch := make(chan bool, 1)
  	var f func() = func() {
  		x = 2
  		ch <- true
  	}
  	go func() {
  		defer func() {
  			err := recover()
  			if err != "sync: negative WaitGroup counter" {
  			}
  			go f()
  		}()
  		x = 1
  		wg.Add(-1)
  	}()
  
  	<-ch
  }
  
  func TestNoRaceWaitGroupTransitive(t *testing.T) {
  	x, y := 0, 0
  	var wg sync.WaitGroup
  	wg.Add(2)
  	go func() {
  		x = 42
  		wg.Done()
  	}()
  	go func() {
  		time.Sleep(1e7)
  		y = 42
  		wg.Done()
  	}()
  	wg.Wait()
  	_ = x
  	_ = y
  }
  
  func TestNoRaceWaitGroupReuse(t *testing.T) {
  	const P = 3
  	var data [P]int
  	var wg sync.WaitGroup
  	for try := 0; try < 3; try++ {
  		wg.Add(P)
  		for p := 0; p < P; p++ {
  			go func(p int) {
  				data[p]++
  				wg.Done()
  			}(p)
  		}
  		wg.Wait()
  		for p := 0; p < P; p++ {
  			data[p]++
  		}
  	}
  }
  
  func TestNoRaceWaitGroupReuse2(t *testing.T) {
  	const P = 3
  	var data [P]int
  	var wg sync.WaitGroup
  	for try := 0; try < 3; try++ {
  		wg.Add(P)
  		for p := 0; p < P; p++ {
  			go func(p int) {
  				data[p]++
  				wg.Done()
  			}(p)
  		}
  		done := make(chan bool)
  		go func() {
  			wg.Wait()
  			for p := 0; p < P; p++ {
  				data[p]++
  			}
  			done <- true
  		}()
  		wg.Wait()
  		<-done
  		for p := 0; p < P; p++ {
  			data[p]++
  		}
  	}
  }
  
  func TestRaceWaitGroupReuse(t *testing.T) {
  	const P = 3
  	const T = 3
  	done := make(chan bool, T)
  	var wg sync.WaitGroup
  	for try := 0; try < T; try++ {
  		var data [P]int
  		wg.Add(P)
  		for p := 0; p < P; p++ {
  			go func(p int) {
  				time.Sleep(50 * time.Millisecond)
  				data[p]++
  				wg.Done()
  			}(p)
  		}
  		go func() {
  			wg.Wait()
  			for p := 0; p < P; p++ {
  				data[p]++
  			}
  			done <- true
  		}()
  		time.Sleep(100 * time.Millisecond)
  		wg.Wait()
  	}
  	for try := 0; try < T; try++ {
  		<-done
  	}
  }
  
  func TestNoRaceWaitGroupConcurrentAdd(t *testing.T) {
  	const P = 4
  	waiting := make(chan bool, P)
  	var wg sync.WaitGroup
  	for p := 0; p < P; p++ {
  		go func() {
  			wg.Add(1)
  			waiting <- true
  			wg.Done()
  		}()
  	}
  	for p := 0; p < P; p++ {
  		<-waiting
  	}
  	wg.Wait()
  }
  

View as plain text