Adam
Adam

Reputation: 3288

Sync WaitGroup not waiting for goroutine to assign pointer

Say I have the following code (playground):

package main

import (
    "fmt"
    "sync"
)

func createStr() *string {
    tmp := "foo"
    return &tmp
}

func main() {
    var (
        s  *string
        wg sync.WaitGroup
    )

    go func() {
        wg.Add(1)
        defer wg.Done()

        s = createStr()
    }()

    wg.Wait()
    fmt.Printf("s after: %v", s)
}

I would have expected s to not equal nil.

However, if I add a small wait, I get s != nil (playground):

package main

import (
    "fmt"
    "sync"
    "time"
)

func createStr() *string {
    tmp := "foo"
    return &tmp
}

func main() {
    var (
        s  *string
        wg sync.WaitGroup
    )

    go func() {
        wg.Add(1)
        defer wg.Done()

        s = createStr()
    }()

    wg.Wait()
    time.Sleep(time.Second)

    fmt.Printf("s after: %v", s)
}

This just caused a bug in a program I wrote. What is happening with sync.WaitGroup that's not causing my program to wait for s to be assigned a string pointer in my go func?

Upvotes: 0

Views: 389

Answers (1)

shmsr
shmsr

Reputation: 4204

Your placement of Add method for sync.WaitGroup is wrong. Do not use Add inside a goroutine (anonymous goroutine here) but use it in the goroutine (main goroutine here) that's going to wait for it.

A probable situation that was happening in your code was that wg.Wait() didn't wait as the goroutine's wg.Add(1) wasn't called yet and hence s == nil. The following code fixes the issue:

Go Playground

package main

import (
    "fmt"
    "sync"
)

func createStr() *string {
    tmp := "foo"
    return &tmp
}

func main() {
    var (
        s  *string
        wg sync.WaitGroup
    )
    // Use wg.Add() here
    wg.Add(1)
    go func() {
        defer wg.Done()
        s = createStr()
    }()
    wg.Wait()
    fmt.Printf("s after: %v", *s)
}

Upvotes: 2

Related Questions