hhdidid
hhdidid

Reputation: 35

Why write channel blocked in spite of a goroutine is selecting on this channel?

I am confused about the following code, I write down some comment in the code which point out my confusion. And there is a result of execution at the end of the code, I also write down what result I expect.

package main

import (
    "fmt"
    "time"
)

func sendRPC() bool {
    time.Sleep(5 * time.Second)
    return true
}

func main() {
    done := make(chan struct{})
    ch := make(chan bool)

    go func() { // goroutine A
        select {
        case ch <- sendRPC():
            fmt.Println("RPC return")
        case <-done:
            fmt.Println("exit")
        }
    }()

    select {
    case <-ch:
    case <-time.After(1000 * time.Millisecond):
        fmt.Println("timeout")
        if len(done) == 0 {
            fmt.Println("1")
            // here write done channel will block until sendRPC() return, why?
            // I expect that the write is nonblock because goroutine A is select on done channel.
            done <- struct{}{}
            fmt.Println("2")
        }
    }

    // result:
    // timeout (after about 1 second)
    // 1
    // exit    (after about 5 seconds, I expect that it is printed after about 1 second too.)
    // 2

}

Upvotes: 1

Views: 76

Answers (1)

Thundercat
Thundercat

Reputation: 121199

The specification says:

For all the cases in the statement, the channel operands of receive operations and the channel and right-hand-side expressions of send statements are evaluated exactly once, in source order, upon entering the "select" statement. The result is a set of channels to receive from or send to, and the corresponding values to send. Any side effects in that evaluation will occur irrespective of which (if any) communication operation is selected to proceed.

The set of channels for the select in goroutine A waits on the evaluation of sendRPC(). It may help to look at this equivalent goroutine:

go func() { // goroutine A
    v := sendRPC()  // waits for 5 seconds
    select {
    case ch <- v:
        fmt.Println("RPC return")
    case <-done:
        fmt.Println("exit")
    }
}()

The receive on done is delayed for 5 seconds.

Upvotes: 1

Related Questions