1

Let's say I have 2 goroutines, one running in another, each needs a waitgroup to prevent goroutine leak. Here is the code:

func A(wg *sync.WaitGroup) {
    defer wg.Done()
    // do something
}

func B(wg *sync.WaitGroup) {
    defer wg.Done()
    wg.Add(1)  // <- this is for A()
    go A(wg)
    // do something
}

func main() {
    wg := &sync.WaitGroup{}
    wg.Add(1)  // <- this is for B()
    go B(wg)
    // do something
    wg.Wait()
}

It works well. But it is possible that the wg.Add(1) line inside B() is called after the wg.Wait() in main() started. The code is still working well in that case. But this seems to be a potential race condition to me.

Notice that this is not violating the WaitGroup rule: "calls with a positive delta that occur when the counter is zero must happen before a Wait". Here the Add() inside B() is happening when the counter is 1.

My question is: is it ok to call wg.Add() in one goroutine when wg.Wait() is blocking in another goroutine? If not, what's the best practice here? Apparently I can choose to not share one wairgroup for both A and B, instead to use 2 waitgroups for them respectively, but it will be pretty messy, especially when there are even more functions and we end up with a lot waitgroups to wait.

David M
  • 433
  • 1
  • 4
  • 10
  • 1
    Your usecase is 100% supported and that's why the rule you quoted is written as it is :) – hobbs May 11 '23 at 18:03

1 Answers1

4

It is ok to call wg.Add while another goroutine is waiting on wg.Wait. There are no race conditions, and it will work fine as long as wg does not reach zero before wg.Add is called, because that will release wg.Wait. In your usage, there is no way wg can reach zero, because even if goroutine B terminates before A starts, wg would not reach zero because it is added before the new goroutine starts.

Burak Serdar
  • 46,455
  • 3
  • 40
  • 59