Sridhar
Sridhar

Reputation: 2532

How to implement non-blocking write to an unbuffered channel?

From Effective Go

Receivers always block until there is data to receive. If the channel is unbuffered, the sender blocks until the receiver has received the value.

But signal.Notify relays signals to an unbuffered channel without blocking. How does this work and is it possible to do with other chan<- types ?

Upvotes: 23

Views: 23825

Answers (4)

lucaswxp
lucaswxp

Reputation: 2119

So, this doesn't answer directly to the problem since it asks for a unbuffered channel, but if all you want is for it not to block on send, this is what I came up with:

ch := make(chan shared.BlogPost, 1024)

You just put a number larger than expected. If you don't know in advance the maximum capacity, then this solution might not work for you.

Also remember that go allocates the channel eagerly, so be careful with memory usage.

Upvotes: 0

VonC
VonC

Reputation: 1328522

Which is why the documentation for Notify explicitly states that you should use a buffered channel

With Go 1.17, the tool vet will also make that clearer:

New warning for calling signal.Notify on unbuffered channels

The vet tool now warns about calls to signal.Notify with incoming signals being sent to an unbuffered channel.

Using an unbuffered channel risks missing signals sent on them as signal.Notify does not block when sending to a channel.

For example:

c := make(chan os.Signal)
// signals are sent on c before the channel is read from.
// This signal may be dropped as c is unbuffered.
signal.Notify(c, os.Interrupt)

Users of signal.Notify should use channels with sufficient buffer space to keep up with the expected signal rate.

Upvotes: 0

Jeremy Huiskamp
Jeremy Huiskamp

Reputation: 5304

You can always avoid blocking while (probably) still guaranteeing delivery by using another goroutine:

go func() { channel <- message }()

Of course, this is just using the goroutine scheduler as a substitute buffer for your channel, which may or may not be wise.

Upvotes: 5

Adrian
Adrian

Reputation: 46562

What it means when it says os.Notify will not block is the messages will be dropped were it to block. So while it's true that it doesn't block, it's not true that it will relay the signals if they can't be received immediately. This is done via simple select:

select {
    case channel <- message:
        // message sent
    default:
        // message dropped
}

Which is why the documentation for Notify explicitly states that you should use a buffered channel. Also note that buffered channels can also block, not just unbuffered channels; buffered channels only block if the buffer is already full.

select is covered in the tour and the spec.

Upvotes: 45

Related Questions