Why does the use of an unbuffered channel in the same goroutine result in a deadlock?

Salah Eddine Taouririt picture Salah Eddine Taouririt · Sep 6, 2013 · Viewed 23.5k times · Source

I'm sure that there is a simple explanation to this trivial situation, but I'm new to the go concurrency model.

when I run this example

package main

import "fmt"

func main() {
    c := make(chan int)    
    c <- 1   
    fmt.Println(<-c)
}

I get this error :

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
    /home/tarrsalah/src/go/src/github.com/tarrsalah/tour.golang.org/65.go:8 +0x52
exit status 2

Why ?


Wrapping c <- in a goroutine makes the example run as we expected

package main

import "fmt"

func main() {
    c := make(chan int)        
    go func(){
       c <- 1
    }()
    fmt.Println(<-c)
}

Again, why ?

Please, I need deep explanation , not just how to eliminate the deadlock and fix the code.

Answer

Denys S&#233;guret picture Denys Séguret · Sep 6, 2013

From the documentation :

If the channel is unbuffered, the sender blocks until the receiver has received the value. If the channel has a buffer, the sender blocks only until the value has been copied to the buffer; if the buffer is full, this means waiting until some receiver has retrieved a value.

Said otherwise :

  • when a channel is full, the sender waits for another goroutine to make some room by receiving
  • you can see an unbuffered channel as an always full one : there must be another goroutine to take what the sender sends.

This line

c <- 1

blocks because the channel is unbuffered. As there's no other goroutine to receive the value, the situation can't resolve, this is a deadlock.

You can make it not blocking by changing the channel creation to

c := make(chan int, 1) 

so that there's room for one item in the channel before it blocks.

But that's not what concurrency is about. Normally, you wouldn't use a channel without other goroutines to handle what you put inside. You could define a receiving goroutine like this :

func main() {
    c := make(chan int)    
    go func() {
        fmt.Println("received:", <-c)
    }()
    c <- 1   
}

Demonstration