Search code examples
goconcurrencychannelgoroutine

why channel is not receiving being member of struct


Code : https://play.golang.org/p/Oh3oTa7GIPX

type a struct {
    c chan bool
}

func (a *a) do() {
    a.c <- true
}

type b struct {
    c chan bool
    a a
}

func main() {
    b := b{
        c: make(chan bool),
        a: a{c: make(chan bool)},
    }

    go b.s()
    b.c <- true

    // below is to stay main gorutine alive
    done := make(chan bool)
    go func() {
        time.Sleep(10 * time.Second)
        done <- true
    }()
    for {
        select {
        case <-done:
            fmt.Println("Done!")
            return
        }
    }
}

func (b *b) s() {
    for {
        select {
        case <-b.c:
            fmt.Println("b c")
            b.a.do()

        case <-b.a.c:
            fmt.Println("b a c")
        }
    }
}

Actual Output of above is

b c
Done!

Expected Ouput:

b c
b a c
Done !

I am not getting why is it not printing b a c ?

Code is self explanatory, if it still requires more details please ask


Solution

  • Your main goroutine sends a value on b.c, then waits:

    b.c <- true
    

    The goroutine you launch from main:

    go b.s()
    

    This is the one that receives from b.c, and also from b.a.c:

    func (b *b) s() {
        for {
            select {
            case <-b.c:
                fmt.Println("b c")
                b.a.do()
    
            case <-b.a.c:
                fmt.Println("b a c")
            }
        }
    }
    

    If a value is received from b.c, this goroutine attempts to send on b.a.c (in a.do() method), and you expect this same goroutine to receive from b.a.c. But since b.a.c is unbuffered, the send will block, so it will never reach the next iteration in b.s() where it could / would receive from b.a.c.

    If a channel is unbuffered, a send on it may only proceed if there is another goroutine ready to receive from it.

    If you make the b.a.c channel buffered, a send on it can proceed without blocking, so in the next iteration it can be received:

    a: a{c: make(chan bool, 1)}
    

    With this change you get your expected output. Try it on the Go Playground.