why <-(chan x)(nil) blocks?

2674 views
Skip to first unread message

Dustin Sallings

unread,
Nov 14, 2012, 6:26:24 PM11/14/12
to golan...@googlegroups.com

Is there anything written on why a receive from nil would block
instead of panicking? It seems like this would always be a bug.

--
dustin

Dave Cheney

unread,
Nov 14, 2012, 6:29:09 PM11/14/12
to Dustin Sallings, golan...@googlegroups.com
because

var x chan int

select {
case <- x:
// never fires
default:
// always fires
}

which has some useful properties
> --
>
>

David Symonds

unread,
Nov 14, 2012, 6:29:13 PM11/14/12
to Dustin Sallings, golan...@googlegroups.com
On Thu, Nov 15, 2012 at 10:26 AM, Dustin Sallings <dsal...@gmail.com> wrote:

> Is there anything written on why a receive from nil would block
> instead of panicking? It seems like this would always be a bug.

It is more convenient for it to block. It allows for some elegant
algorithms involving a loop around a select block.

Dustin Sallings

unread,
Nov 14, 2012, 6:44:25 PM11/14/12
to golan...@googlegroups.com
Dave Cheney <da...@cheney.net> writes:

> because
>
> var x chan int
>
> select {
> case <- x:
> // never fires
> default:
> // always fires
> }
>
> which has some useful properties

Yes that totally makes sense, but in that case, <-x actually works
differently than it does outside of a select block even when it's not
nil.

Since the behavior of an otherwise blocking select is already
different when in a select block, it seems that saying "nil is ignored"
(which the select spec basically says) is really an independent concern
from a plain <-nil -- which is almost definitely a bug if it ever occurs
in software.

--
dustin

Thomas Bushnell, BSG

unread,
Nov 14, 2012, 10:26:41 PM11/14/12
to Dustin Sallings, golang-nuts

I use this frequently. Have a function which accepts an optional channel, and reads from it in a select. Callers who don't need to send on it just pass nil, and the right thing automatically happens.

Thomas

--


Dustin Sallings

unread,
Nov 14, 2012, 10:44:03 PM11/14/12
to golan...@googlegroups.com
"Thomas Bushnell, BSG"
<tbus...@google.com> writes:

> I use this frequently. Have a function which accepts an optional
> channel, and reads from it in a select. Callers who don't need to send
> on it just pass nil, and the right thing automatically happens.

Yes -- I think I had a pretty good example of using it in select in a
thread here in the last couple of days.

select semantics are different, though, to the point where the spec
specifically calls out what a nil case in a select means.

My question is regarding a bare nil receive (or send, for that
matter). I'm having trouble imagining a case where someone does this
and it's not a serious bug.

--
dustin

Jesse McNelis

unread,
Nov 14, 2012, 10:58:02 PM11/14/12
to Dustin Sallings, golang-nuts
On Thu, Nov 15, 2012 at 2:44 PM, Dustin Sallings <dsal...@gmail.com> wrote:
  select semantics are different, though, to the point where the spec
specifically calls out what a nil case in a select means.
 
The select semantics are the same:
var x chan int

select {
case <- x:
   // never fires
}

  My question is regarding a bare nil receive (or send, for that
matter).  I'm having trouble imagining a case where someone does this
and it's not a serious bug.

Should the above select panic?
 


--
=====================
http://jessta.id.au


Dustin Sallings

unread,
Nov 14, 2012, 11:07:52 PM11/14/12
to golan...@googlegroups.com
Jesse McNelis <jes...@jessta.id.au> writes:

> On Thu, Nov 15, 2012 at 2:44 PM, Dustin Sallings
> <dsal...@gmail.com> wrote:
>
>   select semantics are different, though, to the point where the
> spec
> specifically calls out what a nil case in a select means.
>
>  
> The select semantics are the same:
> var x chan int
>
> select {
> case <- x:
>    // never fires
> }

That's correct it never fires in that case, exactly as it says in the
spec:

"If there are no cases with non-nil channels, the statement
blocks forever."

It also says:

"A channel may be nil, which is equivalent to that case not
being present in the select statement except if a send, its
expression is still evaluated."

To me, that says, "nil channels are completely ignored in select
statements."

To argue that the ones outside of select statements have to do a
certain thing because of how they're ignored within select statements
doesn't make that much sense to me.

> Should the above select panic?

Absolutely, unless someone can show me a case (again, not in a select)
where behavior other than panic would be useful.

--
dustin

Jesse McNelis

unread,
Nov 14, 2012, 11:15:47 PM11/14/12
to Dustin Sallings, golang-nuts
On Thu, Nov 15, 2012 at 3:07 PM, Dustin Sallings <dsal...@gmail.com> wrote:
  It also says:

        "A channel may be nil, which is equivalent to that case not
        being present in the select statement except if a send, its
        expression is still evaluated."

  To me, that says, "nil channels are completely ignored in select
statements."

They aren't ignored, they just don't change the semantics of the select statement.
A select statement with no cases will block forever.
If it contains a receive from a nil channel(that would block forever) the semantics are the same.

Receiving from a nil channel blocks forever whether or not it's in a select.


=====================
http://jessta.id.au


Dustin Sallings

unread,
Nov 14, 2012, 11:20:04 PM11/14/12
to golan...@googlegroups.com
Jesse McNelis <jes...@jessta.id.au> writes:

>         "A channel may be nil, which is equivalent to that case
> not
>         being present in the select statement except if a send,
> its
>         expression is still evaluated."
>
>   To me, that says, "nil channels are completely ignored in select
> statements."
>
>
> They aren't ignored, they just don't change the semantics of the
> select statement.
> A select statement with no cases will block forever.
> If it contains a receive from a nil channel(that would block forever)
> the semantics are the same.

I'm not a spec writer, just pointing out the text around nil channels
from the select specification.

> Receiving from a nil channel blocks forever whether or not it's in a
> select.

Not if there's a default. This implies select has magical
properties.

...but none of this helps me understand why anything other than a
panic() should occur if someone tries to send or receive from nil. It
will never do anything anyone wants.

--
dustin

Rob Pike

unread,
Nov 14, 2012, 11:39:15 PM11/14/12
to Dustin Sallings, golan...@googlegroups.com
It's for consistency with select. The semantics of a nil channel are
the same regardless of how it is used. It's useful that it blocks in a
select, so that's what it does outside a select.

If it panicked outside a select, not only would it be inconsistent but
the channel code would need to behave differently in the two cases, a
needless complexity.

That's why.

-rob

Jesse McNelis

unread,
Nov 14, 2012, 11:40:31 PM11/14/12
to Dustin Sallings, golang-nuts
On Thu, Nov 15, 2012 at 3:20 PM, Dustin Sallings <dsal...@gmail.com> wrote:
  ...but none of this helps me understand why anything other than a
panic() should occur if someone tries to send or receive from nil.  It
will never do anything anyone wants.

It's probably not something you don't want to do. But Go offers quite a few ways to block a goroutine forever.
eg. 
select{}
or 
a := make(chan int)
<-a





 
--
=====================
http://jessta.id.au


roger peppe

unread,
Nov 15, 2012, 2:54:36 AM11/15/12
to Dustin Sallings, golang-nuts
in fact panicking was the original behaviour. it was changed
for the sake of consistency, as rob says.

i do kinda regret it - the panic-on-receive-from-nil-channel
behaviour did catch quite a few bugs early for me.
however, at least it's easy to see where this has happened,
by glancing at the goroutines' stack trace.
> --
>
>

Øyvind Teig

unread,
Nov 15, 2012, 3:03:43 AM11/15/12
to golan...@googlegroups.com
Since Go does not have conditional expressions in select, nil channels may be used to "simulate" them.

Kyle Lemons' comment at (June 14th 2012) at https://groups.google.com/d/msg/golang-nuts/qa2p0PRY0WE/EYM4ZV_3jisJ shows this. I have elaborated on this in [1].

(Aside: about the real way to get the effect of boolean expressions in guards: 
Of course, if an original request could cause a following session, sending over the channels needed for the session with the original request is another way of not listening on the clients one would want to lock out. This is, I believe the Go idiom for conditional expressions in Go. Another rationale for not having boolean expressions there is that they would have to be side effect free (all values should be invariant at the time when the select is evaluated, no expression in one guard could be allowed to change the preconditions for an other). Go does not have side effect free evaluations. I have discussed this in a blog [2].)

[1] - I the usage of nil-channels in my paper "XCHANs: Notes on a New Channel Type" at http://www.teigfam.net/oyvind/pub/CPA2012/paper.pdf (page 169):

This Go example uses blocking select (with no default case) with one output and one
input. Go “simulates” a guard if a communication component is nil, so when not valid
(line 08-09) only the input line 15 would ever execute. Line 12 will always listen (?) on the
channel, while line 15 then may send (if not nil). In the receive statement of lines 12 we
have dropped an optional second parameter, so we assume the channel does not become
closed.

01 func Server (in <-chan int, out chan<- int) {
02     value := 0 // Declaration and assignment
03     valid := false // --“--
04     for {
05         outc := out // Always use a copy of "out"
06         // If we have no value, then don't attempt
07         // to send it on the out channel:
08         if !valid {
09             outc = nil // Makes input alone in select
10         }
11         select {
12?            case value = <-in: // RECEIVE?
13                 // "Overflow" if valid is already true.
14                 valid = true
15!            case outc <- value: // SEND?
16             valid = false
17         }
18     }
19 }

Dustin Sallings

unread,
Nov 15, 2012, 3:06:23 AM11/15/12
to golan...@googlegroups.com
roger peppe <rogp...@gmail.com> writes:

> in fact panicking was the original behaviour. it was changed
> for the sake of consistency, as rob says.
>
> i do kinda regret it - the panic-on-receive-from-nil-channel
> behaviour did catch quite a few bugs early for me.
> however, at least it's easy to see where this has happened,
> by glancing at the goroutines' stack trace.

This is exactly why I raised the question. Someone came into irc
asking what was wrong with his program. He had a global buffered
channel, spawned a goroutine that read from it, then did two sends in
main and it deadlocked.

A few of us looked at the code for awhile, but nobody noticed the : in
the initialization until we saw the stack that said it was sending and
receiving from nil. In this case, because it was a deadlock, it was
easy enough to find, but a panic would've been more obvious.

I wasn't expecting a change, just wondering why the thing that would
point out when someone wrote something that is obviously a bug wouldn't
just do that (or if I was missing a case where it wasn't a bug).

--
dustin

Thomas Bushnell, BSG

unread,
Nov 15, 2012, 11:31:38 AM11/15/12
to Dustin Sallings, golang-nuts
I would hate for these two constructs to do different things. That's why, for me, it is obviously right that a read from a nil channel blocks:

a <- b

and

select {
case a<-b:
}

Thomas



--
dustin

--



Dustin Sallings

unread,
Nov 15, 2012, 12:45:14 PM11/15/12
to golan...@googlegroups.com
"Thomas Bushnell, BSG"
<tbus...@google.com> writes:

> I would hate for these two constructs to do different things. That's
> why, for me, it is obviously right that a read from a nil channel
> blocks:
>
> a <- b
>
> and
>
> select {
> case a<-b:
> }

Why would you write the first case and find your goroutine hanging
forever to be desirable? I sense that you're passionate about this
behavior, but I just don't understand why it's so important to you as I
can only see that as being a bug.

--
dustin

Thomas Bushnell, BSG

unread,
Nov 15, 2012, 12:52:54 PM11/15/12
to Dustin Sallings, golang-nuts
I want both cases to do the same thing, whatever that is.

And if you want
select {
  case a<-b:
}

to panic, then I also want

select {
  case a<-b:
  default:
}

to panic.  Select avoids blocks, it does not somehow prevent whatever the channel normally does from happening.



--
dustin

--



Dustin Sallings

unread,
Nov 15, 2012, 1:18:08 PM11/15/12
to golan...@googlegroups.com
"Thomas Bushnell, BSG"
<tbus...@google.com> writes:

> I want both cases to do the same thing, whatever that is.

Surely you can see how this is kind of evading my question.

> And if you want
> select {
>   case a<-b:
> }
>
> to panic, then I also want
>
> select {
>   case a<-b:
>   default:
> }
>
> to panic.  Select avoids blocks, it does not somehow prevent whatever
> the channel normally does from happening.

The channel operation normally blocks. Select makes the thing the
channel normally does not happen unless it can send or receive
immediately.

Again, the select documentation specifically says it ignores nil
channels, so it's special-cased already in the language. If the first
case above began to panic and the second case didn't, I don't honestly
think anyone would be surprised and the select portion of the spec would
still be correct.

This is why I am asking for operations *not* in a select. We already
have the answer that some find it more consistent and it simplifies the
channel code. I don't expect a change. I'm mainly just curious as to
whether anyone would ever have a case where it was desirable. So far,
I've had three people suggest it'd make bug finding easier if it
panicked, but nobody saying, "but I rely on that behavior for {{.}}."

Anyway, I'll stop contributing to this conversation now. I'm still
interested in hearing a place where someone finds this useful just for
satisfying my own curiosity, but I'll save the list any more
repetitions of clarification of my question.

Thanks for yours and all the other insight I've got.

--
dustin

Thomas Bushnell, BSG

unread,
Nov 15, 2012, 1:29:21 PM11/15/12
to Dustin Sallings, golang-nuts
On Thu, Nov 15, 2012 at 10:18 AM, Dustin Sallings <dsal...@gmail.com> wrote:
  Again, the select documentation specifically says it ignores nil
channels, so it's special-cased already in the language.  If the first
case above began to panic and the second case didn't, I don't honestly
think anyone would be surprised and the select portion of the spec would
still be correct.

It's not a "special case", it's just an explanation of what is already clearly required by the semantics
of the general rule (receives from nil block; select avoids blocks).

Thomas

minux

unread,
Nov 15, 2012, 1:47:26 PM11/15/12
to Dustin Sallings, golan...@googlegroups.com
On Fri, Nov 16, 2012 at 2:18 AM, Dustin Sallings <dsal...@gmail.com> wrote:
  Again, the select documentation specifically says it ignores nil
channels, so it's special-cased already in the language.  If the first
no, this is not really a special case. because it just follows from the fact
that receiving from nil channel always blocks, then the select case for a
nil channel naturally won't fire at all, so it is effectively ignored.
I think the spec just want to make this corner case clear.

if you really want the runtime to panic when receiving from a nil channel
to aid debugging, i think you just need to do a one line change:

diff -r dc4a3f6ba179 src/pkg/runtime/chan.c
--- a/src/pkg/runtime/chan.c    Wed Nov 14 09:42:48 2012 -0800
+++ b/src/pkg/runtime/chan.c    Fri Nov 16 02:45:45 2012 +0800
@@ -308,7 +308,8 @@
                        *selected = false;
                        return;
                }
-               runtime·park(nil, nil, "chan receive (nil chan)");
+               runtime·panicstring("receive from nil channel");
+               //runtime·park(nil, nil, "chan receive (nil chan)");
                return;  // not reached
        }
 
if you only think panic on receiving from nil channel is good for
debugging, then this is the solution for you.

Øyvind Teig

unread,
Nov 15, 2012, 4:46:44 PM11/15/12
to golan...@googlegroups.com
..but the example I gave is an example that perhaps should not be 101% ignored?

-Øyvind

stevewang

unread,
Nov 16, 2012, 5:28:48 AM11/16/12
to golan...@googlegroups.com
Is blocking on a nil channel a must in your example?
Is there any replacement for it?

Øyvind Teig

unread,
Nov 16, 2012, 6:19:41 AM11/16/12
to golan...@googlegroups.com, Dustin Sallings
kl. 18:53:07 UTC+1 torsdag 15. november 2012 skrev Thomas Bushnell, BSG følgende:
I want both cases to do the same thing, whatever that is.

And if you want
select {
  case a<-b:
}

to panic, then I also want

select {
  case a<-b:
  default:
}

to panic.  Select avoids blocks, it does not somehow prevent whatever the channel normally does from happening.

I am afraid that this may miss the point. Select is a mechanism to introduce blocking until one communication is possible. The default clause converts the goroutine into a poller. Some times one may want to poll by using default, some times with an alternative timeout in the select. But most of the time the blocking mechanism in select, as well as the blocking mechanism in channels is what it's been designed for. If they didn't have the blocking mechanism many other paradigms could have been selected. (Observe the WYSIWYG semantics with blocking zero-buffered channels.) With all my 10+ years of occam programming with the exact same mechanism (where "default" in "ALT" is "TRUE & SKIP"), I can't remember that I needed to poll with TRUE & SKIP any time. This may reflect my coding style and type of application, but still. The CSP paradigm and a CSP type operating system lets channels drive the action, not the application driving itself by doing busy-poll. So, there is no real need for polling in the scheduler either. (I think somebody in golang-nuts confirmed this for the Go scheduler as well. But there could be other reasons to poll than to "drive" the channel handling, and with that the scheduling?)

- Øyvind

Øyvind Teig

unread,
Nov 16, 2012, 6:28:01 AM11/16/12
to golan...@googlegroups.com
Chicken or egg. The example is there to show exactly that solution. But the alternatives are discussed in that paper. 

About the 101%: I just got engaged that commenters here still touted that it is an error and that there was no use for it. How real the example is, I admit, may be discussed. But silence to the example wasn't golden in my ears. (Personally I would have liked Go to have conditional side effect free expressions in selecet statements, see the blog.)

- Øyvind

stevewang

unread,
Nov 16, 2012, 8:52:04 AM11/16/12
to golan...@googlegroups.com
Being finished with this thread and some older ones, I think it's seldom a bug when you try to receive data from a nil channel.
Usually we get a valid channel by using make function where we declare it:
ch := make(chan int)
It can't be nil in this case.

Sometimes, we have a field of channel type in a struct where it's declaration and initialization are separated:
type T struct {
  ch chan int
}
func NewT() *T {
  return &T{ch:make(chan int)}
}
The field ch could be nil only when we forget to invoke NewT() or forget to invoke make() in NewT().
But I don't think it will really often happen for a little experienced go programmer.

Furthermore, blocking on a nil channel is a useful feature which can be used to implement guarded select elegantly, as Russ Cox posted:

https://groups.google.com/forum/?fromgroups=#!topic/golang-nuts/ChPxr_h8kUM

func maybe(b bool, c chan int) chan int {
    if !b {
        return nil
    }
    return c
}

select {
case <-maybe(val>0, p):
    val--
case <-v:
    val++
}

So my conclusion is that it's a useful feature rather than an inappropriate design.

Dustin Sallings

unread,
Nov 16, 2012, 2:02:33 PM11/16/12
to golan...@googlegroups.com
stevewang <steve....@gmail.com>
writes:

> func maybe(b bool, c chan int) chan int {
>     if !b {
>         return nil
>     }
>     return c
> }
>
> select {
> case <-maybe(val>0, p):
>     val--
> case <-v:
>     val++
> }
>
> So my conclusion is that it's a useful feature rather than an
> inappropriate design.

*sigh*

I understand how it's useful in select. You're providing alternatives
and nil has special consideration directly in the spec to declare it
ignored exactly in the case you have shown. I use nil in select and
find it useful.

I've been asking specifically for a case where it's not in a select
and not a bug, as the current behavior causes the current goroutine to
hold its resources and block forever with no way to ever proceed short
of killing the process.

I do understand that there are other ways to do this as well, but when
I see "select{}", I know exactly what that will do in every case.
However, when I see "<-x", I generally assume something will be read
from channel x. If you take the select out of your example above,
*sometimes* it will read and return and *sometimes* it will hang that
goroutine forever. I am having difficulty imaginging a situation where
this is desirable.

This conversation doesn't seem to be particularly constructive as it
doesn't seem that the implementation is going to change. I was just
hoping I could get some insight on a way to make something useful in a
way I'm incapable of seeing on my own.

For example, a library shouldn't panic(). But are there cases where a
library panics() and it's not a bug in the library? Yes! When the
library is doing something simple (e.g. division) where an error can
only happen if a user very obviously misuses the library (e.g. a
denominator of zero -- which is what math/big does).

--
dustin

minux

unread,
Nov 16, 2012, 2:25:13 PM11/16/12
to Dustin Sallings, golan...@googlegroups.com

On Saturday, November 17, 2012, Dustin Sallings wrote:
stevewang <steve....@gmail.com>
writes:

> func maybe(b bool, c chan int) chan int {
>     if !b {
>         return nil
>     }
>     return c
> }
>
> select {
> case <-maybe(val>0, p):
>     val--
> case <-v:
>     val++
> }
>
> So my conclusion is that it's a useful feature rather than an
> inappropriate design.

  *sigh*

  I understand how it's useful in select.  You're providing alternatives
and nil has special consideration directly in the spec to declare it
more than one people have explained why this is *NOT* a special
consideration at all.
ignored exactly in the case you have shown.  I use nil in select and
find it useful.

  I've been asking specifically for a case where it's not in a select
and not a bug, as the current behavior causes the current goroutine to
hold its resources and block forever with no way to ever proceed short
of killing the process.
there are other programming errors that will make a goroutine block
forever (e.g. deadlock). why do you think we should change the behavior
to solve only this problem (at the cost of bringing inconsistencies to
the spec and make the "special consideration" really special? is this
your point?)

  I do understand that there are other ways to do this as well, but when
I see "select{}", I know exactly what that will do in every case.
However, when I see "<-x", I generally assume something will be read
from channel x.  If you take the select out of your example above,
maybe this only waits for the close of x. 
*sometimes* it will read and return and *sometimes* it will hang that
goroutine forever.  I am having difficulty imaginging a situation where
this is desirable.

  This conversation doesn't seem to be particularly constructive as it
doesn't seem that the implementation is going to change.  I was just
hoping I could get some insight on a way to make something useful in a
way I'm incapable of seeing on my own.
this is your definition of constructive. why are you asking to change the
language when you know it cannot change for Go 1?

people have given you the rationale behind the behavior,
and i even post a patch if you want to panic when receiving from nil channels
to facilitate debugging.

i don't know why this discussion is not constructive.
  For example, a library shouldn't panic().  But are there cases where a
library panics() and it's not a bug in the library?  Yes!  When the
library is doing something simple (e.g. division) where an error can
only happen if a user very obviously misuses the library (e.g. a
denominator of zero -- which is what math/big does).
how about regexp.MustCompile or log.Fatal?
that rule is simply not strict.

stevewang

unread,
Nov 16, 2012, 2:43:20 PM11/16/12
to golan...@googlegroups.com


On Saturday, November 17, 2012 3:04:59 AM UTC+8, Dustin wrote:
stevewang <steve....@gmail.com>
writes:

> func maybe(b bool, c chan int) chan int {
>     if !b {
>         return nil
>     }
>     return c
> }
>
> select {
> case <-maybe(val>0, p):
>     val--
> case <-v:
>     val++
> }
>
> So my conclusion is that it's a useful feature rather than an
> inappropriate design.

  *sigh*

  I understand how it's useful in select.  You're providing alternatives
and nil has special consideration directly in the spec to declare it
ignored exactly in the case you have shown.  I use nil in select and
find it useful.

  I've been asking specifically for a case where it's not in a select
and not a bug, as the current behavior causes the current goroutine to
hold its resources and block forever with no way to ever proceed short
of killing the process.
It's a bug, a bug which the compiler and runtime environment are not responsible for.
As I said above, it's not common and not easy to make a nil channel and then make a goroutine block on it forever.
The one to blame is the guy who writes the buggy code, but not go lang.

Dustin Sallings

unread,
Nov 16, 2012, 2:47:00 PM11/16/12
to golan...@googlegroups.com
minux <minu...@gmail.com> writes:

> more than one people have explained why this is *NOT* a special
> consideration at all.

I disagree with this point, though I don't think it matters much.

The spec clearly says nil is ignored. The fact that whether or not
it's ignored would produce the same effect with nil blocks is an
argument for consistency, but it's not an argument for it not being a
special case.

I would argue, however, that if it *does* produce the same effect, the
wording in the spec is superfluous and mildly confusing. If something
doesn't matter (anymore), perhaps it makes sense to just not document
it.

> there are other programming errors that will make a goroutine block
> forever (e.g. deadlock). why do you think we should change the
> behavior
> to solve only this problem (at the cost of bringing inconsistencies to
> the spec and make the "special consideration" really special? is this
> your point?)

I didn't ask to change the behavior. I asked for an example where the
behavior is desirable.

> I see "select{}", I know exactly what that will do in every case.
> However, when I see "<-x", I generally assume something will be
> read
> from channel x.  If you take the select out of your example above,
>
> maybe this only waits for the close of x. 

If x is nil, any attempts to close it will panic.

Otherwise, I use channels for the sole purpose of closing them quite
frequently.

>   This conversation doesn't seem to be particularly constructive
> as it doesn't seem that the implementation is going to change.  I
> was just hoping I could get some insight on a way to make
> something useful in a way I'm incapable of seeing on my own.
>
> this is your definition of constructive. why are you asking to change
> the language when you know it cannot change for Go 1?

My definition of constructive is, "can someone please show me how I
might use this feature?"

How you get "please change the language at my whim" out of this
request is confusing to me.

>   For example, a library shouldn't panic().  But are there cases
> where a library panics() and it's not a bug in the library?  Yes!
>  When the library is doing something simple (e.g. division) where
> an error can only happen if a user very obviously misuses the
> library (e.g. a denominator of zero -- which is what math/big
> does).
>
> how about regexp.MustCompile or log.Fatal?
> that rule is simply not strict.

Yes, that's why I used it as an example.

I assert that <-nil *outside of a select* is always a bug. I'm hoping
the activity on this thread dies down unless someone can show me an
example where it's not.

I'm not asking for a language change, a justification for the
implementation, an opinion on other ways to write bugs, etc... I just
want to know if I'm missing something. If I'm not, then there's really
nothing to discuss. If I am, I'm happy to learn something new.

--
dustin

Dustin Sallings

unread,
Nov 16, 2012, 2:48:57 PM11/16/12
to golan...@googlegroups.com
stevewang <steve....@gmail.com>
writes:

> It's a bug, a bug which the compiler and runtime environment are not
> responsible for.

> As I said above, it's not common and not easy to make a nil channel
> and then make a goroutine block on it forever. The one to blame is
> the guy who writes the buggy code, but not go lang.

I agree with everything you said.

--
dustin

Thomas Bushnell, BSG

unread,
Nov 16, 2012, 2:57:49 PM11/16/12
to Dustin Sallings, golang-nuts
On Fri, Nov 16, 2012 at 11:47 AM, Dustin Sallings <dsal...@gmail.com> wrote:
minux <minu...@gmail.com> writes:

> more than one people have explained why this is *NOT* a special
> consideration at all.

  I disagree with this point, though I don't think it matters much.

  The spec clearly says nil is ignored.  The fact that whether or not
it's ignored would produce the same effect with nil blocks is an
argument for consistency, but it's not an argument for it not being a
special case.

If the spec didn't say that, the only plausible interpretation would be exactly the same.

You are asking for "a <- b" and "select { case a<-b: }" to do different things. That's ludicrous in my book. It would be the creation of a special case.

  I didn't ask to change the behavior.  I asked for an example where the
behavior is desirable.

It is desirable that "a <- b" and "select {case a<-b:}" do the same thing.

  My definition of constructive is, "can someone please show me how I
might use this feature?"

You've been shown how it's useful inside select. I submit it is also useful that select does not change the semantics of the code within it at all.

  I assert that <-nil *outside of a select* is always a bug.  I'm hoping
the activity on this thread dies down unless someone can show me an
example where it's not.

What if I _want_ a goroutine to block forever? 

stevewang

unread,
Nov 16, 2012, 3:07:16 PM11/16/12
to golan...@googlegroups.com
Then you don't have to ask the runtime to make reading from a nil channel behave differently in different situations so as to explicitly inform the programmer of an apparent bug. 

Dustin Sallings

unread,
Nov 16, 2012, 3:11:27 PM11/16/12
to golan...@googlegroups.com
"Thomas Bushnell, BSG"
<tbus...@google.com> writes:

> If the spec didn't say that, the only plausible interpretation would
> be exactly the same.

I haven't disagreed with this.

> You are asking for "a <- b" and "select { case a<-b: }" to do
> different things. That's ludicrous in my book. It would be the
> creation of a special case.

You're aware that

a := x.(t)

can panic and

a, b := x.(t)

can't, right? That is a lot more cognitive load than "reading from
nil panics, and select ignores nils."

The patterns for "non-blocking send" and "non-blocking receive"
already cause the behavior to be slightly different. I don't think it's
ludicrous to have a thing that can only be a bug be raised as such.

> It is desirable that "a <- b" and "select {case a<-b:}" do the same
> thing.

I wouldn't be offended if they both panicked if whichever one of those
is the channel were nil, to be honest.

> What if I _want_ a goroutine to block forever? 

I think writing "select{}" is idiomatic and thus more recognizable
than writing "<-(chan x)(nil)" if that's all you want.

I don't feel that having lots of ways to cause something to hang
justifies the existence of all of them, but this is the closest thing
I've got to "why I'd ever use this."

How about this... for the purposes of killing off this discussion, I
will accept your answer with the following code:


// Wait until the given amount of time passes, or forever in the case of 0
var ch chan time.Time
if d > 0 {
ch = time.After(d)
}
<-ch

--
dustin

Aram Hăvărneanu

unread,
Nov 16, 2012, 6:22:37 PM11/16/12
to Dustin Sallings, golan...@googlegroups.com
>> It is desirable that "a <- b" and "select {case a<-b:}" do the same
>> thing.
>
> I wouldn't be offended if they both panicked if whichever one of those
> is the channel were nil, to be honest.

I would. Toggling select cases in a loop is extremely useful behavior.

--
Aram Hăvărneanu

Bryan Mills

unread,
Nov 17, 2012, 1:01:08 AM11/17/12
to golan...@googlegroups.com
Without blocking nils you'd still be able to toggle selects - you'd just need to make non-nil channels for the cases you wanted to block.

The big difference would just be that reading from a nil channel would no longer act like reading from an empty channel. If you look carefully, reads from other nil objects (slices and maps) act like reads from empty objects of those types, so the current nil channel behavior is at least consistent with those.

Dustin Sallings

unread,
Nov 17, 2012, 2:49:46 AM11/17/12
to golan...@googlegroups.com
Bryan Mills <donh...@gmail.com> writes:

> The big difference would just be that reading from a nil channel would
> no longer act like reading from an empty channel. If you look
> carefully, reads from other nil objects (slices and maps) act like
> reads from empty objects of those types, so the current nil channel
> behavior is at least consistent with those.

I think that's a great consistency argument, except the difference in
the map and slice cases is that it behaves more like a closed channel
than a nil channel.

e.g.:

for _ = range x {
}

if x is a nil []bool or a nil map[bool]bool, the code in the middle
doesn't run. If x is a nil chan bool, it blocks forever. If x is a
closed chan bool, it doesn't run. If x is a non-closed chan bool, it
waits for a message and if that message is anything but close, it runs
the loop, otherwise it breaks the loop.

--
dustin

Øyvind Teig

unread,
Nov 17, 2012, 2:03:50 PM11/17/12
to golan...@googlegroups.com
This triggered my interest. Short example code and rationale, even if it's a side step?
- Øyvind

Øyvind Teig

unread,
Nov 17, 2012, 2:06:23 PM11/17/12
to golan...@googlegroups.com
That was a comment to Aram Hăvărneanu, seems like the iPad client didn't show that?
- Øyvind

Dustin Sallings

unread,
Nov 17, 2012, 3:51:11 PM11/17/12
to golan...@googlegroups.com
Øyvind Teig <oyvin...@teigfam.net>
writes:

> This triggered my interest. Short example code and rationale, even if
> it's a side step?

It's not *exactly* the same thing, but an example from another thread
looks something like the code below.

I found this to be a natural way to express a simple two-state state
machine where both states have to process input whenever it's ready.
The "init" state is waiting for a timer signal to align with some
wall-clock time or something similar and then the "normal" state
receives ticks regularly.

This is easily expressed with three channels where one of them is nil
at any given iteration.




func processThings(in <-chan stuff) {

var tick <-chan time.Time
var tickinit := time.After(syncupTime)

for {
select {

// Always a valid case -- process any incoming values.
case thing <- in:
store(thing)

// Initially nil while waititing for the init signal, then
// always not nil
case <-tick:
doPeriodicStuff()

// initially not nil, but nils itself out after receives the
// initialization signal
case <-tickinit:
// Configure the repeat timer
tick = time.Tick(duration)
// Disable this case.
tickinit = nil
}

}
}


--
dustin

Øyvind Teig

unread,
Nov 17, 2012, 4:41:43 PM11/17/12
to golan...@googlegroups.com
Thanks, Dustin! This is nice, I like it. Occam could have used something like do_tick & void ? tick, and do_periodic & sometimeproperty ? tickinit (? is channel input) in the ALT guards for this. That's more explicit. Luckily your example did not contain outputs in the guards, which occam would have had to simulate. As I have learned Go simulates conditional expression in guards (when nil). In this case the occam pattern for simulating output guards is more complicated than the Go for condition expressions. But my "xchan" simulates output guard easier, when there is none (and gives a somewhat different semantics) (ref. paper). I am so glad to see all discussions about Go, and hear a professor at the university here in Trondheim the other night state that the studens love Go! In recent years both Go and XC make me hopeful. I certainly stay tuned!

- Øyvind

Bryan Mills

unread,
Nov 18, 2012, 4:14:03 PM11/18/12
to golan...@googlegroups.com
Reads from a nil channel behave like reads from an empty channel, not a closed channel:

Øyvind Teig

unread,
Nov 19, 2012, 3:10:06 AM11/19/12
to golan...@googlegroups.com
Use of oral language is interesting, and in this case seems to reflect some of the internals of Go. You say "reads of a nil/empty channel". But there are no "reads", meaning there is no communication going on in your example. But, the runtime system must still read the channel struct to see if it's empty (more later) or nil, or has/takes data. Since I seem to have given myself a role as referring to occam (I may be wearing some of you out, but it is on good and bad, and seeing one from the other is a learning experience - I am counting on that effect also in Go programmers who did not have the opportunity to use occam 1985-2000), the optional conditional boolean expression in guards in that language just tests the local condition. If it the side effect free expression evaluates to false, the run-time system does not look in the channel struct at all. This would mean that it won't see the state of the channel either, but then - that's perhaps what encapsulation is about. However, the Go designers probably have done this deliberately, to be able to do the nil / closed test? I assume this would be the same if the occam channel were "mobile", introduced in occam at a time when it was out of commercial use, by the University of Kent, UK. Since occam (at all times) had channel usage checks (you can't do the plumbing wrong, so it's impossible to both use the sender and the receiving end of a channel), that would rule out some of the testing that Go opens for. But Go is more flexible, with the down side meaning that you can also do more wrong.

In one way your example shows nothing else than zero==zero, since there is no communication and in both cases there is this dubious polling select with default. The default case really obfuscates the effect of an empty channel, since an empty channel is one where there has been sent no data or which is not ready to receive. The select is supposed to wait for that data (arriving or ready to be sent), in a set of channels. For error handling close to the terminals of a system the select default is perhaps a useful special case, but for normal Goroutine channel interchange its use should be avoided in my opinion. 

- Øyvind

samsal...@gmail.com

unread,
Oct 3, 2014, 7:20:24 AM10/3/14
to golan...@googlegroups.com
Interesting conversation. As a relatively new Go programmer, this has caught me by surprise quite a few times. Reading from nil does feel intuitively like it should panic to me, since it is an operation that can never yield. An empty channel can at least in theory eventually receive and unblock.

I wonder what the cognitive load would be if attempting to read (in a select or anywhere) from a nil channel was guaranteed to panic, no matter where. Then, if you really wanted to use the blocking behaviour, you could use a simple if/else:

func aFunc(c chan struct{}) {
    if c == nil { select{} }
    else { doSomething(<-c) }
}

so: aFunc(nil) would block forever, whereas:

    c := make(chan struct{}, 1)
    c <- struct{}{}
    aFunc(c)

would call doSomething(<-c)

This makes it really explicit that you intend to hang forever in the case of nil.

Under this scheme, the following contrived example would always panic, since it conceptually is trying to read from nil.

    select {
        case <-(chan struct{})(nil): // panic
        default: doSomething()
    }

I know this has been thought about deeply and the point argued to death, I just think an explicit if/else block is much simpler and less surprising than blocking on a nil channel.

Likewise, in a loop, instead of selecting on everything, you can use if/else to handle nil channels:

    func selector(c1 chan int, c2 chan int) {
        for {
            if c1 != nil && c2 != nil {
                select {
                case <-c1: A()
                case <-c2: B()
                default: C()
                }
            } else if c1 !=  nil {
                select {
                case <-c1: A()
                default: C()
                }
            } else if c2 != nil {
                select {
                case <-c2: B()
                default: C()
                }
            } else {
                C()
            }
        }   
    }

Or provide default values for nil channels, for much greater concision:

    func conciseSelector(c1 chan int, c2 chan int) {
        blocker := make(chan struct{})
        if c1 == nil { c1 = blocker }
        if c2 == nil { c2 = blocker }
        for {
            select {
            case <-c1: A()
            case <-c2: B()
            default: C()
            }
        }   
    }

I agree that this is less neat than blocking on nil, but for me it's also much less surprising. I guess it comes down to which is the more common use-case. So far I've only needed non-nil channels for all the things I've written in Go, so blocking on nil has never seemed like a useful feature to me, but perhaps my coding is atypical (I'm writing batch data processors and web servers).

Does anyone have an example of a piece of useful code they have written that makes good use of blocking on a nil channel? Especially a case that would be very difficult to write, or inefficient, using default blocking channels or if/else blocks. I'd be much happier with the pain it is causing me if I at least knew this was making someone else's life easier somewhere :)

Alternatively, if channels were structs whose zero value was an unbuffered channel, that might also avoid some of the confusion, at least in the use cases I encounter frequently.

Thanks,
Sam

Ian Lance Taylor

unread,
Oct 3, 2014, 9:24:22 AM10/3/14
to samsal...@gmail.com, golang-nuts
On Fri, Oct 3, 2014 at 4:20 AM, <samsal...@gmail.com> wrote:
>
> Does anyone have an example of a piece of useful code they have written that
> makes good use of blocking on a nil channel? Especially a case that would be
> very difficult to write, or inefficient, using default blocking channels or
> if/else blocks. I'd be much happier with the pain it is causing me if I at
> least knew this was making someone else's life easier somewhere :)

I think it's unlikely that anybody has a good example of blocking on a
nil channel. But there are many examples of using nil channels in
select, and blocking on a nil channel is a consequence of the same
behaviour--a nil channel is never ready to read.

Don't get me wrong, we could separate the behaviour--we could treat
nil channels differently in select and in a direct read operation.
And perhaps we should have, but it's too late now.

Ian

Sam Salisbury

unread,
Oct 3, 2014, 9:47:05 AM10/3/14
to golan...@googlegroups.com, samsal...@gmail.com
I'm definitely not suggesting special handling in select statements, but rather a guaranteed panic whenever code is encountered that could, or attempts to read from a nil channel. So, a panic reading from a nil channel in a select, as well as a panic reading from a nil channel not in a select. i.e.:

    <-(chan int)(nil) // panics
    select { case <-(chan int)(nil): } // panics
    select {
        case <-(chan int)(nil):
        case <-make(chan int):
    } // panics, even though the second case could have blocked

Are you able to point me to an example of code using nil channels in a select that would have been prohibitively difficult to implement in a world where attempting to receive on a nil channel in a select would panic?

I know it's too late to change the spec, at least for 1.x versions of Go, but I'd really like a better grasp of when this is useful, as I think I may be missing out on some nice patterns...

Thanks,
Sam

James Bardin

unread,
Oct 3, 2014, 9:56:55 AM10/3/14
to golan...@googlegroups.com, samsal...@gmail.com



Are you able to point me to an example of code using nil channels in a select that would have been prohibitively difficult to implement in a world where attempting to receive on a nil channel in a select would panic?


I've seen used a few time where setting a chan variable to nil is used to "turn on" and "turn off" receiving on a particular channel:

var disabled chan int
for {
select {
case x := <-chan1:
// do something
// turn chan2 back on if needed
case x := <-chan2:
// do something
}

// now temporarily stop receiving from chan2
disabled = chan2
chan2 = nil
}

Ian Lance Taylor

unread,
Oct 3, 2014, 9:58:13 AM10/3/14
to Sam Salisbury, golang-nuts
On Fri, Oct 3, 2014 at 6:47 AM, Sam Salisbury <samsal...@gmail.com> wrote:
>
> Are you able to point me to an example of code using nil channels in a
> select that would have been prohibitively difficult to implement in a world
> where attempting to receive on a nil channel in a select would panic?

I'm not sure what you mean by "prohibitely difficult," but there is
plenty of code that takes advantage of the fact that nil channels in a
select statement are effectively ignored (never ready to read/write).
The first example I found in the standard library is
persistConn.roundTrip in net/http/transport.go. Look at the handling
of failTicker and respHeaderTimer.

Ian

Øyvind Teig

unread,
Oct 3, 2014, 4:28:26 PM10/3/14
to golan...@googlegroups.com, samsal...@gmail.com
I think this was also mentioned in this thread in 2012. Anyhow, that's what you would do in occam and CSPm: select specifies a list of guarded commands. The guards are a combination of an optional boolean condition and an input or output communication. But Go uses nil channels to get this, which is just another way. Having a boolean expression is perhaps more explicit and you don't have to nil the channel, but again, I think you can do the same.

The discusson here about whether to panic or "block" (=hang), isn't that really a discussion of whether to have deadlock detection? "Blocking" and deadlock are feel and lookalikes, but are on different planets. I know that Go some times says at runtime that the system is deadlocked (I think it's when it knows that the system can't proceed) - but deadlcok is basically analyzed with a different set of tools. Go could be translated to some modeling language and verified (like Promela/Spin, CSPm/FDR3 or PAT). (Promela/Spin is the work of Holzmann and I think also Pike was on it when he was young: http://swtch.com/~rsc/thread/). Several systems do this formal verification under the hood already.

I have no issue with Go on how it's done, and they explicitly have not done it like below (which I guess is how a European Go might have looked like). rec and send need to be updated elsewhere of course:
var c, c1, c2, c3 chan int
var i1, i2 int
rec, send := true, true
select {
case rec && i1 = <-c1:
    print("received ", i1, " from c1\n")
case send && c2 <- i2:
     print("sent ", i2, " to c2\n")
case i3, ok := (<-c3): // same as: i3, ok := <-c3
    if ok {
        print("received ", i3, " from c3\n")
    } else {
        print("c3 is closed\n")
    }
}
Reply all
Reply to author
Forward
0 new messages