fatal error: all goroutines are asleep – deadlock

Issue

I’ve the following Go code

package main

import (
    "fmt"
    "math/rand"
)

const (
    ROCK int = iota
    PAPER
    SCISSORS
)

type Choice struct {
    Who   int //0 you 1 your opponent
    Guess int
}

//Win returns true if you win.
func Win(you, he int) bool {
    ...
}

func Opponent(guess chan Choice, please chan struct{}) {
    for i := 0; i < 3; i++ {
        <-please
        choice := rand.Intn(3)
        who := 1
        guess <- Choice{who, choice}
        please <- struct{}{}
    }
}

func GetChoice(you, he int) int {
    ...
}

var Cheat func(guess chan Choice) chan Choice = func(guess chan Choice) chan Choice {
    new_guess := make(chan Choice)
    // go func() {
    for i := 0; i < 3; i++ {
        g1 := <-guess
        g2 := <-guess
        if g1.Who == 0 {
            choice := GetChoice(g1.Guess, g2.Guess)
            new_guess <- g2
            new_guess <- Choice{g1.Who, choice}
        } else {
            choice := GetChoice(g2.Guess, g1.Guess)
            new_guess <- g1
            new_guess <- Choice{g2.Who, choice}
        }
    }
    // }()
    fmt.Println("...")
    return new_guess
}

func Me(guess chan Choice, please chan struct{}) {

    for i := 0; i < 3; i++ {
        <-please
        choice := rand.Intn(3)
        who := 0
        guess <- Choice{who, choice}
        please <- struct{}{}
    }
}

func Game() []bool {
    guess := make(chan Choice)
    //please sync 2 goroutines.
    please := make(chan struct{})
    go func() { please <- struct{}{} }()
    go Opponent(guess, please)
    go Me(guess, please)
    guess = Cheat(guess)
    var wins []bool

    for i := 0; i < 3; i++ {
        g1 := <-guess
        g2 := <-guess
        win := false
        if g1.Who == 0 {
            win = Win(g1.Guess, g2.Guess)
        } else {
            win = Win(g2.Guess, g1.Guess)
        }
        wins = append(wins, win)
    }

    return wins
}

func main() {
    win := Game()
    fmt.Println(win)
}

When I run this code I get the error fatal error: all goroutines are asleep – deadlock!. But when I uncomment the go func() line in Cheat function above the error disappear. I couldn’t understand why the error appears in the first case and why it disappears when using goroutine. So if someone could please explain this?

Solution

In this simplified example:

func Cheat(guess chan Choice) chan Choice {
        new_guess := make(chan Choice)
        new_guess <- Choice{}
        <-guess
        return new_guess
}

When the write to the newly allocated channel happens, nobody else can possibly have the channel yet and so that write will block forever. Since that write blocks, the read from guess never happens. But, in the code you quoted, the Cheat() function is the only thing that reads from the guess channel; so the things that are writing to it are blocked on that read happening, that read won’t happen until the write to new_guess happens, and that write can’t happen until the containing function returns.

If you move the channel I/O into a goroutine, then the containing function can return before things progress, and so the write in Cheat() gets paired with the reads at the end of Game() and things can move forward.

Answered By – David Maze

Answer Checked By – Senaida (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.