The following test in Go fails:
type A struct {
b bool
}
func TestWG(t *testing.T) {
var wg sync.WaitGroup
a := update(&wg)
wg.Wait()
if !a.b {
t.Errorf("error")
}
}
// Does not work
func update(group *sync.WaitGroup) A {
a := A{
b : false,
}
group.Add(1)
go func() {
a.b = true
group.Done()
}()
return a
}
Initially I thought this is might be happening due to the absence of barrier in waitGroup.Done()
, which might explain why changing update
to
// works
func update(group *sync.WaitGroup) A {
a := A{
b : false,
}
group.Add(1)
go func() {
a.b = true
group.Done()
}()
time.Sleep(1*time.Second)
return a
}
works. But then changing return type to pointer
also makes it work
// works
func update(group *sync.WaitGroup) *A {
a := A{
b : false,
}
group.Add(1)
go func() {
a.b = true
group.Done()
}()
return &a
}
Can someone tell me what is happening here?
Your first example has a data race!
You return a
which needs to read it, and a concurrent goroutine (which you just launched) writes it without synchronization. So the output is undefined! go test -race
also confirms this.
Same thing goes when you add a sleep: the data race remains (time.Sleep()
is not a synchronization tool), so the result is still undefined! go test -race
confirms this again.
When you change the return type to pointer, then you just return a pointer to a
which does not involve reading the value of a
, and the launched goroutine does not modify the pointer, just the pointed value. And the caller TestWG()
properly waits until its done using the waitgroup, so no data race occurs here.