Shared memory across go routines

Issue

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?

Solution

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.

Answered By – icza

Answer Checked By – Mildred Charles (GoLangFix Admin)

Leave a Reply

Your email address will not be published.