Go design detail rationale question - channel close

1844 views
Skip to first unread message

Jan Mercl

unread,
Mar 18, 2014, 2:03:53 PM3/18/14
to golang-nuts
Can someone of the Go authors (language designers) provide the
motivation for channel closing not being idempotent, please?

Elsewhere I have a discussion on this topic and I'm falling miserably
to communicate what I think is the reason. Either it's my poor English
or I'm just wrong. Especially in the later case I would love to learn
the correct answer.

Thanks in advance to anyone caring to answer.

-j

Ian Lance Taylor

unread,
Mar 18, 2014, 2:28:07 PM3/18/14
to Jan Mercl, golang-nuts
On Tue, Mar 18, 2014 at 11:03 AM, Jan Mercl <0xj...@gmail.com> wrote:
>
> Can someone of the Go authors (language designers) provide the
> motivation for channel closing not being idempotent, please?
>
> Elsewhere I have a discussion on this topic and I'm falling miserably
> to communicate what I think is the reason. Either it's my poor English
> or I'm just wrong. Especially in the later case I would love to learn
> the correct answer.

Here is a motivation:

A channel "close" is really just a send of a special value on a
channel. It is a special value that promises that no more values will
be sent. Attempting to send a value on a channel after it has been
closed will panic, since actually sending the value would violate the
guarantee provided by close. Since a close is just a special kind of
send, it is also not permitted after the channel has been closed.

Here is another:

The only use of channel close is to signal to the reader that there
are no more values to come. That only makes sense when there is a
single source of values, or when multiple sources coordinate. There
is no reasonable program in which multiple goroutines close a channel
without communicating. That would imply that multiple goroutines
would know that there are no more values to send--how could they
determine that if they don't communicate?

Ian

Rob Pike

unread,
Mar 18, 2014, 4:42:58 PM3/18/14
to Ian Lance Taylor, Jan Mercl, golang-nuts
Here is another:

Closing a channel releases it as a resource. It makes no more sense to
close a channel multiple times than it makes to close a file
descriptor multiple times, or free a block of allocated memory
multiple times. Such actions imply the code is broken, which is why
closing a closed channel triggers a panic.

-rob

Nate Finch

unread,
Mar 18, 2014, 5:04:39 PM3/18/14
to golan...@googlegroups.com, Ian Lance Taylor, Jan Mercl
I think there's an interesting point here that I just figured out.  I was thinking of closing a channel like deleting a file.  I don't actually care if the file is deleted *now*, I just care that it doesn't exist after this line of code (barring outside factors recreating the file etc etc).

However, closing a channel is more than that, it's also a synchronization mechanism with consumers.  And that often does matter if it happens *now*.  It's a signal, and if I'm closing it, I expect consumers to see that it was closed *now* and not at some other time.

So, I can sort of see why it panics, especially since it's such a critical synchronization mechanism.

John Souvestre

unread,
Mar 18, 2014, 6:09:44 PM3/18/14
to golang-nuts
Hello Ian.

> That would imply that multiple goroutines would know that there
> are no more values to send--how could they determine that if they
> don't communicate?

Could this happen perhaps if it is a redundant lookup by a group, either for
robustness or for speed?

> Since a close is just a special kind of send, it is also not
> permitted after the channel has been closed.

Since it is special anyway, perhaps make it "extra-special"? :)

John

John Souvestre - New Orleans LA
--
You received this message because you are subscribed to the Google Groups
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Dave Cheney

unread,
Mar 18, 2014, 10:26:35 PM3/18/14
to golan...@googlegroups.com, Ian Lance Taylor, Jan Mercl


On Wednesday, 19 March 2014 08:04:39 UTC+11, Nate Finch wrote:
I think there's an interesting point here that I just figured out.  I was thinking of closing a channel like deleting a file.  I don't actually care if the file is deleted *now*, I just care that it doesn't exist after this line of code (barring outside factors recreating the file etc etc).

However, closing a channel is more than that, it's also a synchronization mechanism with consumers.  And that often does matter if it happens *now*.  It's a signal, and if I'm closing it, I expect consumers to see that it was closed *now* and not at some other time.

I'm not sure that is correct. If I close a channel, any consumer of that will not see that close until

a. they receive from the channel, and
b. the channel is empty

The use of "now" indicating that the receiver receives an immediate notification is quite frequently not true.

Nate Finch

unread,
Mar 19, 2014, 1:17:38 PM3/19/14
to golan...@googlegroups.com, Ian Lance Taylor, Jan Mercl
On Tuesday, March 18, 2014 10:26:35 PM UTC-4, Dave Cheney wrote:

On Wednesday, 19 March 2014 08:04:39 UTC+11, Nate Finch wrote:
I think there's an interesting point here that I just figured out.  I was thinking of closing a channel like deleting a file.  I don't actually care if the file is deleted *now*, I just care that it doesn't exist after this line of code (barring outside factors recreating the file etc etc).

However, closing a channel is more than that, it's also a synchronization mechanism with consumers.  And that often does matter if it happens *now*.  It's a signal, and if I'm closing it, I expect consumers to see that it was closed *now* and not at some other time.

I'm not sure that is correct. If I close a channel, any consumer of that will not see that close until

a. they receive from the channel, and
b. the channel is empty

The use of "now" indicating that the receiver receives an immediate notification is quite frequently not true.

Yes, that's true.  Their goroutine needs to come up in the scheduler, and they need to receive from the channel, and it needs to be empty (I almost never use buffered channels, so sometimes I forget they even exist).  

You're right that my characterization makes it sound too much like an event notification. 

roger peppe

unread,
Mar 22, 2014, 6:39:00 PM3/22/14
to Ian Lance Taylor, Jan Mercl, golang-nuts
On 18 March 2014 18:28, Ian Lance Taylor <ia...@golang.org> wrote:
> On Tue, Mar 18, 2014 at 11:03 AM, Jan Mercl <0xj...@gmail.com> wrote:
>>
>> Can someone of the Go authors (language designers) provide the
>> motivation for channel closing not being idempotent, please?
>>
>> Elsewhere I have a discussion on this topic and I'm falling miserably
>> to communicate what I think is the reason. Either it's my poor English
>> or I'm just wrong. Especially in the later case I would love to learn
>> the correct answer.
>
> Here is a motivation:
>
> A channel "close" is really just a send of a special value on a
> channel. It is a special value that promises that no more values will
> be sent. Attempting to send a value on a channel after it has been
> closed will panic, since actually sending the value would violate the
> guarantee provided by close. Since a close is just a special kind of
> send, it is also not permitted after the channel has been closed.

In my view, close is different from a channel send because
it does not actually send a value. There is almost no correspondence
between the number of times a channel has been closed
and the number of values received.

> Here is another:
>
> The only use of channel close is to signal to the reader that there
> are no more values to come. That only makes sense when there is a
> single source of values, or when multiple sources coordinate. There
> is no reasonable program in which multiple goroutines close a channel
> without communicating. That would imply that multiple goroutines
> would know that there are no more values to send--how could they
> determine that if they don't communicate?

There is at least one counter-example to this, an idiom that
I've seen used regularly, and that was recently demonstrated in a
post on the Go Programming Language blog (http://blog.golang.org/pipelines).

That is, it is perfectly reasonable to use a channel that is never used
to send any values at all, and where close is the only action
performed on the channel. In that context, it can often
be useful to be able to close the channel from two contexts.
Perhaps we have a goroutine sitting in a loop that may possibly
terminate with an error, and also some external agent may want to
terminate the loop itself.

It seems reasonable to me that in that situation, either the
external agent *or* the loop itself can close the channel
that indicates "everything should shut down now".
It doesn't much matter where the stimulus for
the shutdown came from, but if the two events happen
in close proximity, we need to be sure we do not panic.

One idiom I've seen for avoiding this is:

mu.Lock()
select {
case <-c:
// The channel is already closed.
default:
close(c)
}
mu.Unlock()

Another is just to recover from the panic.

But really it would be much nicer if we could just
call close.

On 18 March 2014 20:42, Rob Pike <r...@golang.org> wrote:
> Closing a channel releases it as a resource. It makes no more sense to
> close a channel multiple times than it makes to close a file
> descriptor multiple times, or free a block of allocated memory
> multiple times. Such actions imply the code is broken, which is why
> closing a closed channel triggers a panic.

It actually does make sense to close a file descriptor multiple
times in some circumstances. In Go, the only way to unblock a goroutine blocked
on reading a file descriptor is to close the file descriptor.
But it's quite possible that at the same moment the
we decided to do that, the goroutine completed its action
and went ahead with closing the file descriptor itself.

For this reason, it's useful to make file descriptor closing idempotent.
Such code isn't necessarily broken; neither is code
that closes channels multiple times necessarily broken IMO.

Ian Lance Taylor

unread,
Mar 23, 2014, 5:29:24 PM3/23/14
to roger peppe, Jan Mercl, golang-nuts
On Sat, Mar 22, 2014 at 3:39 PM, roger peppe <rogp...@gmail.com> wrote:
> On 18 March 2014 18:28, Ian Lance Taylor <ia...@golang.org> wrote:
>> On Tue, Mar 18, 2014 at 11:03 AM, Jan Mercl <0xj...@gmail.com> wrote:
>>>
>>> Can someone of the Go authors (language designers) provide the
>>> motivation for channel closing not being idempotent, please?
>>>
>>> Elsewhere I have a discussion on this topic and I'm falling miserably
>>> to communicate what I think is the reason. Either it's my poor English
>>> or I'm just wrong. Especially in the later case I would love to learn
>>> the correct answer.
>>
>> Here is a motivation:
>>
>> A channel "close" is really just a send of a special value on a
>> channel. It is a special value that promises that no more values will
>> be sent. Attempting to send a value on a channel after it has been
>> closed will panic, since actually sending the value would violate the
>> guarantee provided by close. Since a close is just a special kind of
>> send, it is also not permitted after the channel has been closed.
>
> In my view, close is different from a channel send because
> it does not actually send a value. There is almost no correspondence
> between the number of times a channel has been closed
> and the number of values received.

To be clear, I was only providing motivations.

I don't actually understand your comment. A channel can only be
closed once. Naturally there is no correspondence between the number
of times a channel is closed (0 or 1) and the numbers of values
received from a channel.

And I assert that a channel close really does send a value, a special
kind of value that can only be received using a "val, closed = <-"
statement. You can tell it is sending a value because if you send 10
values on a buffered channel and then close it, the receiver, even if
running after the close, will receive 10 values and then the close
notification.


>> Here is another:
>>
>> The only use of channel close is to signal to the reader that there
>> are no more values to come. That only makes sense when there is a
>> single source of values, or when multiple sources coordinate. There
>> is no reasonable program in which multiple goroutines close a channel
>> without communicating. That would imply that multiple goroutines
>> would know that there are no more values to send--how could they
>> determine that if they don't communicate?
>
> There is at least one counter-example to this, an idiom that
> I've seen used regularly, and that was recently demonstrated in a
> post on the Go Programming Language blog (http://blog.golang.org/pipelines).
>
> That is, it is perfectly reasonable to use a channel that is never used
> to send any values at all, and where close is the only action
> performed on the channel. In that context, it can often
> be useful to be able to close the channel from two contexts.
> Perhaps we have a goroutine sitting in a loop that may possibly
> terminate with an error, and also some external agent may want to
> terminate the loop itself.
>
> It seems reasonable to me that in that situation, either the
> external agent *or* the loop itself can close the channel
> that indicates "everything should shut down now".
> It doesn't much matter where the stimulus for
> the shutdown came from, but if the two events happen
> in close proximity, we need to be sure we do not panic.

Yes, I agree that this is a special case of how close can be used, but
as a special case I don't think it provides a particularly strong
argument one way or another. As you point out there are ways to
handle this case today, and there would be other ways to handle it
even if close behaved differently or did not exist at all.


I don't actually feel particularly strongly about the behaviour of
close. I merely think that the current behaviour is justifiable: it
is a reasonable choice among other reasonable choices.

Ian
Reply all
Reply to author
Forward
0 new messages