1

The following code produces an unexpected runtime error:

package main

import (
    "fmt"
    "sync"
)

func receiver(bChan <-chan int, wg *sync.WaitGroup) {
    for val := range <-bChan {
        fmt.Println("received", val)
    }
    wg.Done()
}

func main() {
    wg := &sync.WaitGroup{}
    bChan := make(chan int, 2)
    bChan <- 1
    bChan <- 2

    wg.Add(1)
    go receiver(bChan, wg)
    bChan <- 3
    bChan <- 4
    close(bChan)
    wg.Wait()
}

output:

received 0
fatal error: all goroutines are asleep - deadlock!
goroutine 1 \[chan send\]:

I did fix it after realizing I wrote the for loop wrong. When I Change for val := range <-bChan to for val := range bChan it runs as expected.

Why is there a deadlock? And also why is "received 0" printed?

4
  • 2
    range <-bChan is equivalent to range 1 because 1 is the first value sent on the channel. This simply loops once and the loop value is zero. Commented Jun 12 at 8:35
  • 1
    range <-bChan means you receive from the chan, and then range over the value that was received. It evaluates to range 1 — the first value you sent into the channel. range N means "iterate from 0 to N excluded", hence 0. Commented Jun 12 at 8:35
  • 1
    Thank you for your comments. I understand it now. The for loop evaluates <-bChan first, so I get for val := range 1 {...}, "received 0" is printed and the loop ends. The main routine sends another value bChan <- 3 which is fine, but when it tries to bChan <- 4 the channel is full (its capacity is 2, 2 and 3 are in it) so it blocks and I get a deadlock. Commented Jun 12 at 9:20
  • @AlonKalif correct. actually, feel free to post that as an answer; since this behavior has been enabled only recently when they added range-over-integers to the language, I suspect this question might be useful to others Commented Jun 12 at 13:51

1 Answer 1

1

range <-bChan means you receive from the channel first, and then range over the value that was received.

The first value you sent into the channel is 1, therefore your range clause evaluates to:

for val := range 1

This is syntactically valid since Go 1.22 when the range-over-integer feature was added to the compiler:

For an integer value n, where n is of integer type or an untyped integer constant, the iteration values 0 through n-1 are produced in increasing order.

Thus your loop runs one time with val=0 and prints received 0. Then the program deadlocks because nothing is reading from the channel anymore, but you send more values than what the buffer can hold.

Before Go 1.22 your program would have failed to compile with an error:

cannot range over <-bChan (comma, ok expression of type int)

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.