xohr
xohr

Reputation: 87

WaitGroup - fatal error: all goroutines are asleep - deadlock

Can someone explain why this code throws an "fatal error: all goroutines are asleep - deadlock!"?

I can't seem to find what is wrong. I've seen some questions about this specific error, but the reason was mostly looping through a channel without closing it. Thank you!

package main

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

func main() {

    ch := make(chan time.Duration)

    var wg sync.WaitGroup

    for _, v := range []time.Duration{5, 1} {
        wg.Add(1)
        go wait(v, ch, wg)
        fmt.Println(<-ch)
    }

    wg.Wait()
}

func wait(seconds time.Duration, c chan time.Duration, wg sync.WaitGroup) {

    defer wg.Done()

    time.Sleep(seconds * time.Second)

    c <- seconds
}

Upvotes: 0

Views: 1023

Answers (1)

Eli Bendersky
Eli Bendersky

Reputation: 273416

You have to pass the WaitGroup by reference, not by value. Otherwise the Done has no effect. The documentation of this type says:

A WaitGroup must not be copied after first use.

Fix your code to this and it will work:

func main() {

    ch := make(chan time.Duration)

    var wg sync.WaitGroup

    for _, v := range []time.Duration{5, 1} {
        wg.Add(1)
        go wait(v, ch, &wg)
        fmt.Println(<-ch)
    }

    wg.Wait()
}

func wait(seconds time.Duration, c chan time.Duration, wg *sync.WaitGroup) {

    defer wg.Done()

    time.Sleep(seconds * time.Second)

    c <- seconds
}

It is also common to express this pattern as follows:

func main() {
    ch := make(chan time.Duration)

    var wg sync.WaitGroup

    for _, v := range []time.Duration{5, 1} {
        wg.Add(1)

        go func() {
            defer wg.Done()
            wait(v, ch)
        }()
        fmt.Println(<-ch)
    }

    wg.Wait()
}

func wait(seconds time.Duration, c chan time.Duration) {
    time.Sleep(seconds * time.Second)
    c <- seconds
}

What's nice about this case is that wait doesn't have to be aware of any wait groups (it could be a 3rd-party function, for example), and there's no confusion about passing a wait group by value or reference.

Upvotes: 4

Related Questions