// 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
|
_ = x
|
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
|
_ = x
|
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
|
_ = x
|
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
|
_ = x
|
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
|
_ = x
|
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
|
_ = x
|
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
|
_ = x
|
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
|
_ = x
|
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()
|
}
|