Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How is this chan leaked?

I'm trying to understand the problem outlined on this slide:

http://talks.golang.org/2013/bestpractices.slide#27

Copying the code in case the URL dies:

func sendMsg(msg, addr string) error {
    conn, err := net.Dial("tcp", addr)
    if err != nil {
        return err
    }
    defer conn.Close()
    _, err = fmt.Fprint(conn, msg)
    return err
}

func broadcastMsg(msg string, addrs []string) error {
    errc := make(chan error)
    for _, addr := range addrs {
        go func(addr string) {
            errc <- sendMsg(msg, addr)
            fmt.Println("done")
        }(addr)
    }

    for _ = range addrs {
        if err := <-errc; err != nil {
            return err
        }
    }
    return nil
}

func main() {
    addr := []string{"localhost:8080", "http://google.com"}
    err := broadcastMsg("hi", addr)

    time.Sleep(time.Second)

    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println("everything went fine")
}

And the comments:

  • the goroutine is blocked on the chan write
  • the goroutine holds a reference to the chan
  • the chan will never be garbage collected

I'm not sure I understand why the chan never gets collected or which goroutine is keeping a reference to the chan. Your time is appreciated!

like image 425
mattbasta Avatar asked Aug 01 '13 01:08

mattbasta


1 Answers

The Go Programming Language Specification

Function literals

A function literal represents an anonymous function.

FunctionLit = "func" Function .

func(a, b int, z float64) bool { return a*b < int(z) }

A function literal can be assigned to a variable or invoked directly.

f := func(x, y int) int { return x + y }
func(ch chan int) { ch <- ACK }(replyChan)

Function literals are closures: they may refer to variables defined in a surrounding function. Those variables are then shared between the surrounding function and the function literal, and they survive as long as they are accessible.

Send statements

A send statement sends a value on a channel. The channel expression must be of channel type, the channel direction must permit send operations, and the type of the value to be sent must be assignable to the channel's element type.

SendStmt = Channel "<-" Expression .
Channel  = Expression .

Both the channel and the value expression are evaluated before communication begins. Communication blocks until the send can proceed. A send on an unbuffered channel can proceed if a receiver is ready. A send on a buffered channel can proceed if there is room in the buffer. A send on a closed channel proceeds by causing a run-time panic. A send on a nil channel blocks forever.

There is only one go statement, go func(addr string), and it's a closure over the channel variable errc.

func broadcastMsg(msg string, addrs []string) error {
    errc := make(chan error)
    for _, addr := range addrs {
        go func(addr string) {
            errc <- sendMsg(msg, addr)
            fmt.Println("done")
        }(addr)
    }

    for _ = range addrs {
        if err := <-errc; err != nil {
            return err
        }
    }
    return nil
}

Two goroutines are started since len(addrs) == 2. Because of a premature exit when err != nil on the first receive on channel errc, only one goroutine completes. The second goroutine is blocked on the send (write) to the unbuffered channel errc; it never completes. Therefore, there is still a reference to errc, so it's never garbage collected. The second goroutine is eventually abandoned when the program exits.

like image 199
peterSO Avatar answered Sep 21 '22 19:09

peterSO