1

I've spent pretty significant amount of time trying to debug why my channel doesn't accept anything. I managed to localize the issue as the one related to the scope of named return values when they are returned with naked returns. The below code shows the problem.

package main

import (
    "log"
    "sync"
)

var receiver chan int

func Setup() (receiver chan int) {
    receiver = make(chan int)
    return
}

//func Setup() (chan int) {
//  receiver = make(chan int)
//  return receiver
//}

func Launch(j int){

    for i := 0; i < j; i++ {
        receiver <- i
    }

}

func main() {

    var wg sync.WaitGroup
    wg.Add(10)

    rcvr := Setup()

    go func() {
        for r := range rcvr {
            log.Println(r)
            wg.Done()
        }
    }()

    Launch(10)

    wg.Wait()

 }

Running this code produces the next error

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send (nil chan)]:

...

I cannot grasp why the channel should be nil. I tried similar assignment with primitive values and it returns what I expect. Why is it nil with channels? What shadows what in here?

  • you are creating a new channel not using the one you have assigned in `launch` function that's why the channel is nil. – Himanshu Jul 19 '18 at 09:44
  • I have update the code snippet. Please, pay attention to a channel name in main function. It is now changed to 'rcvr'. Main function and other code are supposed to be in different packages. – Andrei Piakhota Jul 19 '18 at 12:56

1 Answers1

3

Edit: to your edited code:

Now you are using 2 completely different channels. You have one channel in your main(), which you try to receive from (using a for range). And you have another channel (the global receiver variable to which you never assign any values, so it remains nil. And sending values on a nil channel blocks forever (see How does a non initialized channel behave? for details).

So there you have it: Launch() is blocked at sending on a nil channel, and your main() goroutine waits for the other goroutine it launched, which waits for values on the rcvr channel to which no one sends anything.

An easy way would be to pass the channel to Launch() you create in main(), and get rid of the global var:

func Launch(j int, receiver chan int) {
    for i := 0; i < j; i++ {
        receiver <- i
    }
}

And when calling it:

Launch(10, rcvr)

Then it will work, output will be (try it on the Go Playground):

2009/11/10 23:00:00 0
2009/11/10 23:00:00 1
2009/11/10 23:00:00 2
2009/11/10 23:00:00 3
2009/11/10 23:00:00 4
2009/11/10 23:00:00 5
2009/11/10 23:00:00 6
2009/11/10 23:00:00 7
2009/11/10 23:00:00 8
2009/11/10 23:00:00 9

Original answer follows:

The problem is that you use short variable declaration in your main when storing the channel returned by Setup():

receiver := Setup()

This will not assign a value to your global receiver variable, but create a new local variable which shadows the global variable, and your main() function will use this local variable. But your Launch() function will use the global variable (it doesn't see main's local variables).

Use simple assignment, and all will be good:

receiver = Setup()
icza
  • 389,944
  • 63
  • 907
  • 827
  • My mistake in wrong naming of the variable in main. I should have named it different. For example rcvr = Setup(). The issue still remains. If I switch Setup functions with one I had commented out, everything works all right. – Andrei Piakhota Jul 19 '18 at 12:47
  • I have updated the code snippet. Please, take a look. – Andrei Piakhota Jul 19 '18 at 13:00
  • @AndreiPiakhota Edited my answer. – icza Jul 19 '18 at 13:08
  • Thanks a lot for your elaborate answer. But I don't get it. And the place I don't get it is my Setup function that uses a named return value. In this function I have this initialization `receiver = make(chan int)`. What does it initialize? I thought it was my global `receiver` variable, reference to which I then return to main function. From you answer I infer that my global variable has been shadowed by a local variable which I named the same. Therefore my global `receiver` variable has never been touched. Is this the case? – Andrei Piakhota Jul 19 '18 at 14:39
  • @AndreiPiakhota Yes, exactly. Your named result variable shadows the global, so you never touch the global variable. – icza Jul 19 '18 at 14:41
  • Now I get it. Shadowing is something to take a closer look into. Thanks a lot. The question may be considered as answered. – Andrei Piakhota Jul 20 '18 at 07:00
  • @AndreiPiakhota Then you should consider accepting the answer. – icza Jul 20 '18 at 08:31
  • Done. Thanks again. – Andrei Piakhota Jul 20 '18 at 12:32