Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I catch the exception of a channel deadlocking?

Tags:

go

I am learning Go and am working on this lesson from the GoTours. Here's what I have so far.

package main

import (
    "fmt"
    "code.google.com/p/go-tour/tree"
)

// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int) {
    if t != nil {
        Walk(t.Left, ch)
        ch <- t.Value
        Walk(t.Right, ch)
    }
}

func main() {
    var ch chan int = make(chan int)
    go Walk(tree.New(1), ch)
    for c := range ch {
        fmt.Printf("%d ", c)    
    }
}

As you see, I try to test out my Walk function by printing out the values I wrote into a channel. However, I get the following error.

1 2 3 4 5 6 7 8 9 10 throw: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
    main.go:25 +0x85

goroutine 2 [syscall]:
created by runtime.main
    /usr/local/go/src/pkg/runtime/proc.c:221

exit status 2

This error should be expected I think because I never close the channel. However, is there a way I could "catch" this deadlock error and deal with it programmatically?

like image 947
dangerChihuahua007 Avatar asked Dec 28 '12 20:12

dangerChihuahua007


1 Answers

Deadlock is similar to a nil pointer deference in that is represents a BUG in your program. This class of error is usually not recoverable for this reason.

As lbonn mentioned, the problem here is you need to "close(myChan)" your channel. If you do not do this the for-range loop, that the loop will wait for the next element forever.

You can try something like this:

func main() {
    var ch chan int = make(chan int)
    go func() {
        Walk(tree.New(1), ch)
        close(ch)
    }()
    for c := range ch {
        fmt.Printf("%d ", c)
    }
}

If you want to traverse the tree in parallel you will need to make further changes:

package main

import (
    "code.google.com/p/go-tour/tree"
    "fmt"
    "sync"
)

// Walk walks the tree t sending all values
// from the tree to the channel ch.
func Walk(t *tree.Tree, ch chan int, done *sync.WaitGroup) {
    if t != nil {
        done.Add(2)
        go Walk(t.Left, ch, done) //look at each branch in parallel
        go Walk(t.Right, ch, done)
        ch <- t.Value
    }
    done.Done()
}

func main() {
    var ch chan int = make(chan int, 64) //note the buffer size
    go func() {
        done := new(sync.WaitGroup)
        done.Add(1)
        Walk(tree.New(1), ch, done)
        done.Wait()
        close(ch)
    }()
    for c := range ch {
        fmt.Printf("%d ", c)
    }
}
like image 137
voidlogic Avatar answered Jan 15 '23 11:01

voidlogic