| // 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 |
| } |
| |
| // A common WaitGroup misuse that can potentially be caught be the race detector. |
| // For this simple case we must emulate Add() as read on &wg and Wait() as write on &wg. |
| // However it will have false positives if there are several concurrent Wait() calls. |
| func TestRaceFailingWaitGroupWrongAdd(t *testing.T) { |
| c := make(chan bool, 2) |
| var wg sync.WaitGroup |
| go func() { |
| wg.Add(1) |
| wg.Done() |
| c <- true |
| }() |
| go func() { |
| wg.Add(1) |
| wg.Done() |
| c <- true |
| }() |
| 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 |
| } |
| |
| // 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 |
| } |