Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Why does sending to channel is blocked when a panic occurs?

I am going to show a simple compilable code snipped where I get weird behaviour: after I intentionally cause a panic in processData (because I pass nil-pointer) the sending to channel l.done is blocked!

package main

import (
    "context"
    "fmt"
    "time"
)

type Loop struct {
    done chan struct{}
}

type Result struct {
    ID string
}

func NewLoop() *Loop {
    return &Loop{
        done: make(chan struct{}),
    }
}

func (l *Loop) StartAsync(ctx context.Context) {
    go func() {
        defer func() {
            l.done <- struct{}{} // BLOCKED! But I allocated it in NewLoop ctor
            fmt.Sprintf("done")
        }()
        for {
            /**/
            var data *Result
            l.processData(ctx, data) // passed nil
        }
    }()
}

func (l *Loop) processData(ctx context.Context, data *Result) {
    _ = fmt.Sprintf("%s", data.ID) // intentional panic - OK
}
func main() {
    l := NewLoop()
    l.StartAsync(context.Background())
    time.Sleep(10 * time.Second)
}

I can recover() a panic before sending to channel and I get correct error message.
What does happen with channel? It it was closed, I would get panic on sending to closed channel

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

It’s blocking because there isn’t anything receiving from the channel. Both the receive & the send operations on an initialized and unbuffered channel will block indefinitely if the opposite operation is missing. I.e. a send to a channel will block until another goroutine receives from that channel, and, likewise, a receive from a channel will block until another goroutine sends to that channel.

So basically

done := new(chan struct{})
done<-struct{}{}

will block forever unless there is another goroutine that receives from the channel, i.e. <-done. That’s how channels are supposed to behave. That’s how the languages specification defines their behaviour.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading