Why is WaitGroup.Wait() hanging when using it with go test?

Issue

Here’s a simple example of what I mean

package main

import (
    "sync"
    "testing"
    "time"
)

func TestWaitGroup(t *testing.T) {
    var wg sync.WaitGroup
    quitSig := make(chan struct{})
    go func(wg sync.WaitGroup, quitChan, chan struct{}) {
        defer func() {
            t.Log("Done...")
            wg.Done()
            t.Log("Done!")
        }()
        t.Log("waiting for quit channel signal...")
        <-quitChan
        t.Log("signal received")
    }(wg, quitSig)
    time.Sleep(5*time.Second)
    t.Log("Done sleeping")
    close(quitSig)
    t.Log("closed quit signal channel")
    wg.Wait()
    t.Log("goroutine shutdown")
}

When I run this, I get the following

=== RUN   TestWaitGroup
    main.go:18: waiting for quit channel signal...
    main.go:23: Done sleeping
    main.go:25: closed quit signal channel
    main.go:20: signal received
    main.go:14: Done...
    main.go:16: Done!
    

Where it just hangs until it timesout. If you just do defer wg.Done() the same behaviour is observed. I’m running go1.18. Is this a bug or am I using not using WaitGroups properly in this context?

Solution

Two issues:

  • don’t copy sync.WaitGroup: from the docs:

    • A WaitGroup must not be copied after first use.
  • you need a wg.Add(1) before launching your work – to pair with the wg.Done()


wg.Add(1) // <- add this

go func (wg *sync.WaitGroup ...) { // <- pointer
}(&wg, quitSig) // <- pointer to avoid WaitGroup copy

https://go.dev/play/p/UmeI3TdGvhg

Answered By – colm.anseo

Answer Checked By – Mary Flores (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.