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