Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Go deadlock with "fatal error: all goroutines are asleep"

Tags:

go

channel

Here's the relevant excerpt from my code:

func main() {
    quit := make(chan int)

    readyQueue := make(chan Proc)
    runQueue := make(chan Proc)
    waitQueue := make(chan Proc)

    procList := getInitialProcList()
    fmt.Println(procList)

    for _, proc := range(procList) {
        switch {
            case proc.Status == READY:
                readyQueue <- proc
                tick(quit, readyQueue, runQueue, waitQueue)
            case proc.Status == RUN:
                runQueue <- proc
                tick(quit, readyQueue, runQueue, waitQueue)
            case proc.Status == WAIT:
                waitQueue <- proc
                tick(quit, readyQueue, runQueue, waitQueue)
        }
    }

    <-quit // blocks to keep main thread alive
}

func tick(quit chan int, readyQueue chan Proc, runQueue chan Proc, waitQueue chan Proc) {
    select {
    case p := <-readyQueue:
        fmt.Println(p)
    default:
        fmt.Println("[tick] nothing in ready queue")
    }

    select {
    case p := <-waitQueue:
        fmt.Println(p)
    default:
        fmt.Println("[tick] nothing in wait queue")
    }

    select {
    case p := <-runQueue:
        fmt.Println(p)
    default:
        fmt.Println("[tick] nothing in run queue")
    }

    quit <- 0
}

I don't understand why I'm getting the error fatal error: all goroutines are asleep - deadlock! on the line readyQueue <- proc in the above code.

like image 600
Isaac Dontje Lindell Avatar asked Mar 17 '14 01:03

Isaac Dontje Lindell


1 Answers

As far as the code shows, you are never firing up a concurrent reader for any of the channels you create. Because they are unbuffered, any writes to them, will block until someone, somewhere reads from the other end. This is not the case in your code.

If tick is supposed to be the consumer, you should fire it up before going into the loop. And it should then have a loop itself, so it keeps polling the channels for new values.

go tick(quit, readyQueue, runQueue, waitQueue)

for _, proc := range(procList) {
    ....
}

Another immediate solution would be to create all the channels with a buffer of 1.

quit := make(chan int, 1)

readyQueue := make(chan Proc, 1)
runQueue := make(chan Proc, 1)
waitQueue := make(chan Proc, 1)

While this will solve your immediate problem, there are still some other potential issues with your design. We would need to know exactly what it is you are trying to accomplish, in order to give a more well rounded answer.

like image 97
jimt Avatar answered Nov 03 '22 00:11

jimt