Search code examples
goconcurrencychannelgoroutine

Deadlock while using goroutines


I have a program which does 2 things:

  • Read log entries and create logEntry objects
  • Process each logEntry instances

Here, reading is done by separate goroutines and processing of all the read entries is done by a single goroutine.

I'm using a waitgroup - wg to ensure that all the log entries are read before the program quits and a signal channel - done to ensure the processing of the log entries are completed.

The waitgroup is working as expected, however when I call <-done to ensure that the program exits only after the read log files are processed, it throws the error fatal error: all goroutines are asleep - deadlock!.

Could someone please explain why this is happening and how to fix the above error?

main.go:

package main

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

type logEntry struct {
    lines      []string
    created_at string
    line_count int
}

var wg sync.WaitGroup

func main() {
    linesChan := make(chan (logEntry))

    done := make(chan (bool), 1)

    // Process entries from lines
    go func() {
        for c := range linesChan {
            time.Sleep(100 * time.Millisecond)
            fmt.Printf("%v\n", c)
        }

        done <- true
    }()

    // Read lines
    for i := 1; i <= 10; i++ {
        wg.Add(1)
        go func(i int, linesChan chan (logEntry)) {
            read(i, linesChan)
        }(i, linesChan)
    }

    // Wait till all the files are read
    wg.Wait()

    // Wait till all the log entries are processed
    <-done
    close(done)
}

func read(count int, channel chan (logEntry)) {
    fmt.Println(count, "read")
    channel <- logEntry{
        line_count: count,
    }
    wg.Done()
}

Output:

10 read
6 read
3 read
1 read
4 read
8 read
7 read
2 read
5 read
9 read
{[]  10}
{[]  6}
{[]  3}
{[]  1}
{[]  4}
{[]  8}
{[]  7}
{[]  2}
{[]  5}
{[]  9}
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
  .../main.go:44 +0x13a

goroutine 18 [chan receive]:
main.main.func1()
  .../main.go:24 +0x145
created by main.main
  .../main.go:23 +0x9d
exit status 2

Solution

  • in your case you are listening for linesChan, but not close it. You need to close this channel, when all data will be passed. done <- true won't be executed.

    But don't need channel for sync here, sync.WaitGroup{} will be enough.

    package main
    
    import (
        "fmt"
        "sync"
        "time"
    )
    
    type logEntry struct {
        lines      []string
        created_at string
        line_count int
    }
    
    var wg sync.WaitGroup
    
    func main() {
        linesChan := make(chan (logEntry))
    
        // Process entries from lines
        go func() {
            for c := range linesChan {
                time.Sleep(100 * time.Millisecond)
                fmt.Printf("%v\n", c)
            }
        }()
    
        // Read lines
        for i := 1; i <= 10; i++ {
            wg.Add(1)
            go func(i int, linesChan chan (logEntry)) {
                read(i, linesChan)
            }(i, linesChan)
        }
    
        // Wait till all the files are read
        wg.Wait()
    }
    
    func read(count int, channel chan (logEntry)) {
        fmt.Println(count, "read")
        channel <- logEntry{
            line_count: count,
        }
        wg.Done()
    }