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