Why is there no safe way to send to a closed channel?

2,651 views
Skip to first unread message

Dave Grijalva

unread,
Aug 22, 2011, 7:28:51 PM8/22/11
to golang-nuts
I'm trying to understand the design choice for making a send to a
closed channel cause a runtime panic. Similarly, why does closing a
closed channel cause a panic?

I've read on other discussions that the idiomatic way to manage this
is to have the sender close the channel. How does this work if there
are multiple senders?
Another point I read is that I should instead use another channel to
ask the sender to stop sending. This requires the senders to all be
registered with the receivers so the receivers can ask them to stop.
Or something...

It seems like, with more complex relationships between producer and
consumer, developers can't safely close channels without other
synchronization on top, which defeats the point.

-dave

Kyle Lemons

unread,
Aug 22, 2011, 7:36:02 PM8/22/11
to Dave Grijalva, golang-nuts
Only the sole/final sender (or some delegate thereof) should ever close a channel.  Closing a channel is really only necessary as a method of signaling the consumer that there is no more data to come, it doesn't do anything like deallocate the channel or even any buffered messages stored in it.  A consumer shouldn't be using the close mechanism to notify the senders to stop sending; it should have an out-of-band method of doing so, which will then allow it to process any final data that is still queued up for it before they're all done.  It's not usually that difficult to have a waitgroup triggered off of the completion of all of the senders that will close the channel, and it's also not usually difficult to have a select in the senders which allows them to receive a "stop sending data" signal.

~K

Kyle Lemons

unread,
Aug 22, 2011, 7:39:14 PM8/22/11
to Dave Grijalva, golang-nuts
Only the sole/final sender (or some delegate thereof) should ever close a channel.  Closing a channel is really only necessary as a method of signaling the consumer that there is no more data to come, it doesn't do anything like deallocate the channel or even any buffered messages stored in it.  A consumer shouldn't be using the close mechanism to notify the senders to stop sending; it should have an out-of-band method of doing so, which will then allow it to process any final data that is still queued up for it before they're all done.  It's not usually that difficult to have a waitgroup triggered off of the completion of all of the senders that will close the channel, and it's also not usually difficult to have a select in the senders which allows them to receive a "stop sending data" signal.

I should also add that a "stop sending" signal can be achieved by the senders all selecting on a receive from a channel that the receiver can close; this is a really simple way to do a one-to-many one-time-use signal, and the ,ok receive won't introduce the race conditions that things like sync.Cond could.

Dave Grijalva

unread,
Aug 22, 2011, 7:50:53 PM8/22/11
to Kyle Lemons, golang-nuts
Isn't there still a race condition here?

select {
case _, ok := <-stopchan:
if ok {
// mychan is closed right here by other goroutine
mychan <- mydatas
}
default:
}

The other option is to just never close it, but then you have to work around the receiver goroutine that's forever blocked waiting for the next message.  Or introduce a slow spinlock.

It's possible to work around these things, but it seems arbitrarily difficult.  Does anybody know why these restrictions were placed on the language?

-dave

Russ Cox

unread,
Aug 22, 2011, 7:59:53 PM8/22/11
to Dave Grijalva, golang-nuts
On Mon, Aug 22, 2011 at 19:28, Dave Grijalva <dgri...@ngmoco.com> wrote:
> I'm trying to understand the design choice for making a send to a
> closed channel cause a runtime panic.  Similarly, why does closing a
> closed channel cause a panic?

Information on a channel only flows from sender to receiver.
Close is a signal to the *receivers* that the *senders* are done.
Adding some kind of support for detecting send on a closed
channel would be sending information back to a sender.

There is no requirement that channels be closed.
It's fine to just stop using a channel, unlike, say, file descriptors.

> I've read on other discussions that the idiomatic way to manage this
> is to have the sender close the channel.  How does this work if there
> are multiple senders?

They have to coordinate amongst themselves to agree about
when they're done. You're asking for send-on-closed-channel
as that communication, but it's a fairly weak one, and it means
that the channel stops being useful when one sender thinks
everything is done. You more typically want to wait until all
senders think everything is done.

> It seems like, with more complex relationships between producer and
> consumer, developers can't safely close channels without other
> synchronization on top, which defeats the point.

Most of the time you don't even have to worry about this.
When you do, being able to detect a send on a closed
channel would not cover all the cases you'd want to implement
anyway. If you really need communication between the senders,
you should communicate between the senders. There's no
silver bullet here.

Russ

Kyle Lemons

unread,
Aug 22, 2011, 8:10:53 PM8/22/11
to Dave Grijalva, golang-nuts
Isn't there still a race condition here?

Yes, but that's not inherent in the method I proposed.


The only race condition is that the consumer doesn't only get N pieces of data, but it's pretty trivial for it to break from the loop and then go into another for _ := <-in {} to slurp up the remaining ones and not act on them, if you actually wanted that behavior.
~K
Reply all
Reply to author
Forward
0 new messages