Zenitsu Komodo
Zenitsu Komodo

Reputation: 7

Confusing channel behavior in Go

I have the following website status checker code in Go:

package main

import (
    "fmt"
    "net/http"
)

func main() {
    ls := []string{
        "http://google.com",
        "http://facebook.com",
        "http://amazon.com",
        "http://golang.org",
    }
    
    c := make(chan string)

    for _, url := range ls{
        go checkStatus(url,c)
    } 

    fmt.Println(<- c)
    fmt.Println(<- c)
    fmt.Println(<- c)
    fmt.Println(<- c)
    fmt.Println(<- c) 
    
}

func checkStatus(url string, ch chan string) {
    _, err := http.Get(url)
    if err!=nil{
        fmt.Println(url,"not working fine")
        ch <- "might be down!!" 
        return
    }
    fmt.Println(url, "is up!!!")
    ch <- "It is up"
}

Here, I have 5 receivers but 4 senders. Ideally the code should throw an error. But here it is simply blocking indefinitely.

But the below code is throwing error:

package main

import (
    "fmt"
)

func main() {
    channel := make(chan string)
    for i:=0;i<2;i++{
        go func1(i, channel)
    }
    fmt.Println(<- channel) 
    fmt.Println(<- channel)
    fmt.Println(<- channel)
}

func func1(i int,c chan string){
    if i%2==0{
        c <- fmt.Sprintf("Holaa %v",i)
        return 
    }
    c <- fmt.Sprintf("Hello %v",i)
}

Here i have 3 receivers for 2 senders. This is throwing error as expected.

My question is:
Why the first program is not throwing error but the second one does? Even though the number of receivers in both the programs are greater than the number of senders

Upvotes: -5

Views: 57

Answers (1)

FirstOne
FirstOne

Reputation: 6215

That's not a channel issue, but rather a behavior of the deadlock detection.

When you make an http call, it will spawn internal goroutines which will prevent the detection system from accusing the deadlock. It thinks it's possible for one of the goroutines to still send something on the channel.

To better observe this, you can set the iddle connection timeout:

func checkStatus(url string, ch chan string) {
    client := &http.Client{
        Transport: &http.Transport{
            IdleConnTimeout: 5 * time.Second, // Make the cleanup faster
        },
    }
    resp, err := client.Get(url)
    if err != nil {
        fmt.Println(url, "not working fine")
        ch <- "might be down!!"
        return
    }
    defer resp.Body.Close() // btw, you were missing this
    fmt.Println(url, "is up!!!")
    ch <- "It is up"
}

After 5s, you'll see the deadlock crash:

fatal error: all goroutines are asleep - deadlock!

Upvotes: 0

Related Questions