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?
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.