Search code examples
goconcurrencydeadlockgoroutine

Fatal Error - All Goroutines are asleep! Deadlock


I'm confused as to why this code deadlocks. I'm getting a fatal error about the goroutines being asleep. I'm using a waitgroup to synchronize and wait for the goroutines to finish, as well as passing the address of the one waitgroup I created instead of copying. I tried with and without a buffer but still.

package main

import (
    "fmt"
    "sync"
)


func findMax(nums []int32) int32{
    max:=nums[0]
    for _, n := range nums{
        if n > max{
            max = n
        }
    }
    return max
}

func findFreq(nums []int32,  n int32) int32{
    mp := make(map[int32]int32)
    for _, num := range nums{
        if _, ok := mp[num]; ok{
            mp[num]+=1
        }else{
            mp[num]=1
        }
    }
    if f, ok := mp[n]; ok{
        return f
    }else{
        return -1
    }
}

func performWork(ch chan int32, nums []int32, q int32, wg *sync.WaitGroup){
    defer wg.Done()
    seg:=nums[q-1:]
    max:=findMax(seg)
    freq:=findFreq(seg, max)
    ch <- freq
}

func frequencyOfMaxValue(numbers []int32, q []int32) []int32 {
    res := []int32{}
    var wg sync.WaitGroup
    ch := make(chan int32)
    
    for _, query := range q{
        wg.Add(1)
        go performWork(ch, numbers, query, &wg)
    }
    wg.Wait()
    for n := range ch{
        res=append(res, n)
    }
    return res

}
func main() {
  nums := []int32{5,4,5,3,2}
  queries:=[]int32{1,2,3,4,5}
  fmt.Println(frequencyOfMaxValue(nums,queries))
}


Solution

  • The workers are blocked waiting for main goroutine to receive on the channel. The main goroutine is blocked waiting for the workers to complete. Deadlock!

    Assuming that you get past this deadlock, there's another deadlock. The main goroutine receives on ch in a loop, but nothing closes ch.

    Remove the deadlocks by running another goroutine to close the channel when the workers are done.

    for _, query := range q {
        wg.Add(1)
        go performWork(ch, numbers, query, &wg)
    }
    
    go func() {
        wg.Wait() // <-- wait for workers
        close(ch) // <-- causes main to break of range on ch.
    }()
    
    for n := range ch {
        res = append(res, n)
    }