"Go channels are bad and you should feel bad"

2,844 views
Skip to first unread message

andrey mirtchovski

unread,
Mar 2, 2016, 11:53:42 AM3/2/16
to golang-nuts
Subject is the title of the article, provided here without comment:

http://www.jtolds.com/writing/2016/03/go-channels-are-bad-and-you-should-feel-bad/

Nico

unread,
Mar 2, 2016, 1:29:52 PM3/2/16
to golan...@googlegroups.com
TL;DR;

* "Channels are slower than implementing it yourself" (i.e. channels are slower than mutexes)
* "it’s actually somewhat challenging to use channels alongside mutexes and condition variables correctly!"
* "Callbacks are strictly more powerful and don’t require unnecessary goroutines."
* "Almost every other operation in Go has a way to avoid a panic (type assertions have the , ok = pattern, for example), but with channels you just get to deal with it."
* "How could channels be better?"

I find interesting the idea of a "comma, ok" pattern for send statements. Is this something the Go authors have considered before?

Sam Whited

unread,
Mar 2, 2016, 1:52:10 PM3/2/16
to Nico, golang-nuts
On Wed, Mar 2, 2016 at 12:29 PM, Nico <nicolas...@gmail.com> wrote:
> I find interesting the idea of a "comma, ok" pattern for send statements. Is this something the Go authors have considered before?

This has been discussed here and on golang-dev.

TL;DR — Senders close channels, receivers check if they're closed. If
you're the sender and you send on a closed channel we panic because
you're doing it wrong; it's effectively an assertion that makes sure
you're using the pattern the way it was intended to be used.

—Sam



--
Sam Whited
pub 4096R/54083AE104EA7AD3
https://blog.samwhited.com

andrey mirtchovski

unread,
Mar 2, 2016, 1:56:28 PM3/2/16
to Sam Whited, Nico, golang-nuts
As a counterexample, the Go fork used for the Clive OS allows both
ends of a channel to close and propagates error messages on closing:

http://syssoftware.blogspot.com/2015/06/lsub-go-changes.html

Marvin Stenger

unread,
Mar 2, 2016, 1:56:47 PM3/2/16
to golang-nuts
One could lift the send statement to be an boolean expression, I guess.

Nico

unread,
Mar 2, 2016, 1:58:36 PM3/2/16
to Sam Whited, golang-nuts
On 02/03/16 18:51, Sam Whited wrote:
> TL;DR — Senders close channels, receivers check if they're closed. If
> you're the sender and you send on a closed channel we panic because
> you're doing it wrong; it's effectively an assertion that makes sure
> you're using the pattern the way it was intended to be used.

What when there are several senders?

An ok-pattern would help synchronise all the senders when the channel is closed by one of them.

Tapio Raevaara

unread,
Mar 2, 2016, 2:08:29 PM3/2/16
to golang-nuts
I have to admit that I've always wondered why send and receive operations on nil channel don't simply panic. Am I overlooking something obvious here? When is permanent blocking *ever* desirable behaviour in this case?

Egon

unread,
Mar 2, 2016, 2:08:41 PM3/2/16
to golang-nuts, s...@samwhited.com
On Wednesday, 2 March 2016 20:58:36 UTC+2, Nico wrote:
On 02/03/16 18:51, Sam Whited wrote:
> TL;DR — Senders close channels, receivers check if they're closed. If
> you're the sender and you send on a closed channel we panic because
> you're doing it wrong; it's effectively an assertion that makes sure
> you're using the pattern the way it was intended to be used.

What when there are several senders?

Last one sender to leave, turns off the lights, which can be controlled by a atomic int.

Nathan Fisher

unread,
Mar 2, 2016, 2:22:47 PM3/2/16
to Nico, Sam Whited, golang-nuts
Out of curiosity what scenario would you see it being good to have multiple publishers to shutdown a channel?

--
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.

Nico

unread,
Mar 2, 2016, 2:22:52 PM3/2/16
to golan...@googlegroups.com
On 02/03/16 19:08, Egon wrote:
> Last one sender to leave, turns off the lights, which can be controlled by a atomic int.

A.K.A. sync.WaitGroup.

The ok-pattern would make sync.WaitGroup unnecessary in these cases.

Nico

unread,
Mar 2, 2016, 2:23:39 PM3/2/16
to golan...@googlegroups.com
On 02/03/16 18:58, Tapio Raevaara wrote:
> I have to admit that I've always wondered why send and receive operations on nil channel don't simply panic. Am I overlooking something obvious here? When is permanent blocking *ever* desirable behaviour in this case?

It's useful within a select.

Tapio Raevaara

unread,
Mar 2, 2016, 3:16:51 PM3/2/16
to golang-nuts

I don't understand. Doesn't select essentially ignore cases that operate on nil channels anyway? I'm certainly not suggesting that select should panic if one of the channels is nil.

Sam Whited

unread,
Mar 2, 2016, 3:19:43 PM3/2/16
to Tapio Raevaara, golang-nuts
I'm assuming that by "nil" channel you mean one that has nothing in it?

There are lots of use cases for this; in fact, it's an important part
of using channels. For example, you might use this blocking to create
a simple semaphore using channels:

sem := make(chan int, MaxJobs)

func SomeJobHandler(queue chan *Job) {
for job := range queue {
job := job // Copy and shadow request since Go reuses vars
declared in the for statement
sem <- 1// Will block after the buffer is full, eg. when
MaxJobs are running
go func() {
process(job) // Some expensive processing
<-sem
}()
}
}

Of course, this can be handled in other ways, but this is just an example.
Another common example can be found in the time package (the first
case won't be hit until the timeout time has passed):

select {
case <-After(timeout): // This is similar to calling sleep, except
that we can use it in a select statement where we definitely don't
want the read to panic!
return // We've timed out
default:
// Do some work
}

These are just a few simple examples of why we might want to block on
a channel read.

Tapio Raevaara

unread,
Mar 2, 2016, 3:29:22 PM3/2/16
to golang-nuts, tapio.r...@iki.fi


On Wednesday, March 2, 2016 at 10:19:43 PM UTC+2, Sam Whited wrote:
On Wed, Mar 2, 2016 at 12:58 PM, Tapio Raevaara <tapio.r...@iki.fi> wrote:
> I have to admit that I've always wondered why send and receive operations on
> nil channel don't simply panic. Am I overlooking something obvious here?
> When is permanent blocking *ever* desirable behaviour in this case?

I'm assuming that by "nil" channel you mean one that has nothing in it?

No, I mean (chan T(nil)), i.e. a channel that hasn't been initialized. Encountering one is almost certainly a bug, which is why I don't understand why operating on one just silently locks the goroutine instead of panicking. Like I wrote earlier, I don't see how blocking  could ever be desired behaviour in this case.

Sam Whited

unread,
Mar 2, 2016, 3:32:43 PM3/2/16
to Tapio Raevaara, golang-nuts
On Wed, Mar 2, 2016 at 2:18 PM, Sam Whited <s...@samwhited.com> wrote:
> I'm assuming that by "nil" channel you mean one that has nothing in it?

Nevermind, just realized that you meant eg.

var c chan int;

this is different, but still useful.

Eg. imagine you do a select on a nil channel:

func dowork(c chan int) {
select {
case <-c
// Do some work if not nil
default:
return // Don't do anything:
}
}

You don't have to do a nil check on this function, the correct thing
just happens.

—Sam

Nico

unread,
Mar 2, 2016, 3:34:30 PM3/2/16
to golan...@googlegroups.com

Sam Whited

unread,
Mar 2, 2016, 3:38:29 PM3/2/16
to Nico, golang-nuts
On Wed, Mar 2, 2016 at 2:34 PM, Nico <nicolas...@gmail.com> wrote:
> https://talks.golang.org/2013/advconc.slide#29

Exactly, "select never selects a blocking case" is what we're taking
advantage of in the example I sent. If you DO pass in a non-nil
channel, it will be hit, otherwise (if you pass in nil) the default
case will be hit.

—Sam

Tapio Raevaara

unread,
Mar 2, 2016, 4:06:00 PM3/2/16
to golang-nuts, tapio.r...@iki.fi

Why are bringing up select, I'm not suggesting that its semantics should be changed in any way. Select chooses a case where *communication can proceed*, so if an operation on nil channel would panic, select would still ignore those cases, just like it does right now. The following cases are equivalent:

select {
case <-ch:
    break
}

select {
case <-ch:
    break
case <-(chan int(nil)):
}

These would still remain equivalent even if a normal send and receive on a nil channel would panic, because communication on a nil channel cannot proceed.
 
Again, how is it *ever desirable behaviour* that a simple send or receive on a nil channel will just block forever, with no way to recover? Am I missing something here?

Tapio Raevaara

unread,
Mar 2, 2016, 4:14:10 PM3/2/16
to golang-nuts, tapio.r...@iki.fi


On Wednesday, March 2, 2016 at 11:06:00 PM UTC+2, Tapio Raevaara wrote:
select {
case <-ch:
    break
}

select {
case <-ch:
    break
case <-(chan int(nil)):
}

Sorry, that example doesn't make any sense, I'm not sure what strange fumes I was inhaling...

The point is that a case with an operation on a nil channel will never be selected, and that wouldn't change if nil channel operations would panic outside of select. Which is why I don't see how blocking *instead of panicking* on nil channel operation is in any way "useful".

mikespook

unread,
Mar 2, 2016, 4:23:13 PM3/2/16
to Sam Whited, Tapio Raevaara, golang-nuts
If you passed a nil chan into the function `dowork`, as far as I can concern:

Yes, the behavior is correct. 
But,
No, the logic is wrong.

The implicit meaning of `case <-c` would cause a big trouble in the feature.

--
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.



--
Xing Xing (邢星)
mikespook <mike...@gmail.com>
http://mikespook.com


Sam Whited

unread,
Mar 2, 2016, 4:25:21 PM3/2/16
to Tapio Raevaara, golang-nuts
On Wed, Mar 2, 2016 at 3:14 PM, Tapio Raevaara <tapio.r...@iki.fi> wrote:
> The point is that a case with an operation on a nil channel will never be
> selected, and that wouldn't change if nil channel operations would panic
> outside of select. Which is why I don't see how blocking *instead of
> panicking* on nil channel operation is in any way "useful".

That's not how panic works; panic is for things that should _never_
happen (eg. it's like an assert in other languages). It's a way to
fail fast if something catastrophically bad happens (eg. code that's
impossible to hit is hit because someone screwed something up, a
critical error that could lead to a security compromise happened,
etc.). If something were to panic in a select case, we wouldn't want
it to magically recover the panic and not select that case, we'd still
want to know about it.

You're right, the language could have used panic differently and made
reads from nil channels panic and had select catch them, but it
wouldn't be the same language. This was a deliberate choice to make
things like the example I made possible without magically covering up
serious errors.

TL;DR — I think you're thinking that panic is used for something it
wasn't designed for; blocking is much cleaner.

—Sam

Nico

unread,
Mar 2, 2016, 4:28:38 PM3/2/16
to golan...@googlegroups.com
On 02/03/16 21:06, Tapio Raevaara wrote:
> Why are bringing up select?

If send and receive on nil channel didn't block, then the program in https://talks.golang.org/2013/advconc.slide#29 wouldn't work.

> Select chooses a case where *communication can proceed*, so if an operation on nil channel would panic, select would still ignore those cases, just like it does right now.

Just to be clear, a send statement on a closed channel within a select also panics. See:

https://play.golang.org/p/FUbHw_ey8I

package main

func main() {
ch := make(chan struct{})
close(ch)
select {
case ch <- struct{}{}:
default:
}
}


Alex Bligh

unread,
Mar 2, 2016, 4:35:40 PM3/2/16
to Nico, Alex Bligh, golan...@googlegroups.com

On 2 Mar 2016, at 21:28, Nico <nicolas...@gmail.com> wrote:

> Just to be clear, a send statement on a closed channel within a select also panics. See:
>
> https://play.golang.org/p/FUbHw_ey8I
>
> package main
>
> func main() {
> ch := make(chan struct{})
> close(ch)
> select {
> case ch <- struct{}{}:
> default:
> }
> }

func main() {
ch := make(chan struct{})
close(ch)
select {
case ch, ok <- struct{}{}:
default:
}
}

The above looks horrible I grant you, but making that return false in ok rather than
panicing would not (I think) break anything. Currently it gives 'select cases cannot be lists'
obviously.

--
Alex Bligh




Tapio Raevaara

unread,
Mar 2, 2016, 5:00:13 PM3/2/16
to golang-nuts

 Hm... I looked at the spec and I'm wondering *why* that panics. Sending on a closed channel is supposed to panic, but select is only supposed to pick a case where *communication can proceed* (or the default case, of course). Am I misreading the spec somehow?

Oh, and thanks for linking to that program a second time, I totally misread that the first time. Indeed, that program wouldn't work if the semantics were what I'm suggesting, so in a sense the current semantics can be considered "useful". Still, the exact same behaviour would be achieved by wrapping the sends in a select. I maintain that in vast majority of cases trying to operate on a nil channel is *bug* and runtime should panic - like it does if you try to close a nil channel. The fact is that every time you send/receive on a nil channel (without select), you *silently* leak a goroutine.

Dan Kortschak

unread,
Mar 2, 2016, 5:28:35 PM3/2/16
to Tapio Raevaara, golang-nuts
On Wed, 2016-03-02 at 12:29 -0800, Tapio Raevaara wrote:
> No, I mean (chan T(nil)), i.e. a channel that hasn't been
> initialized.
> Encountering one is almost certainly a bug, which is why I don't
> understand why operating on one just silently locks the goroutine
> instead of panicking. Like I wrote earlier, I don't see how blocking
> could ever be desired behaviour in this case.

It can be used to conditionally block a select branch. This is very
useful behaviour.

Dave Cheney

unread,
Mar 2, 2016, 5:28:37 PM3/2/16
to golang-nuts
Have a look back in the git history to the point when send and receive used to work like this. There is lots of historical discussion, pre go 1.0, that explains why things are the way they are.

Nico

unread,
Mar 2, 2016, 5:32:00 PM3/2/16
to Nathan Fisher, Sam Whited, golang-nuts
On 02/03/16 19:22, Nathan Fisher wrote:
> Out of curiosity what scenario would you see it being good to have multiple publishers to shutdown a channel?

Just out of curiosity:

* here's a fan-in pattern that leaks goroutines: https://play.golang.org/p/oYtXpuOoMJ

* and here, using the currently-invalid "comma, ok" pattern on send statements: https://play.golang.org/p/3MC4JSdJ8t

package main

import "fmt"

func main() {
ch := fanIn(seq(5), seq(10))

for i := range ch {
fmt.Println(i)
if i > 5 {
break
}
}

close(ch) // invalid operation: close(ch) (cannot close receive-only channel)
}

func seq(n int) <-chan int {
ch := make(chan int)
go func() {
for i := 0; i < n; i++ {
// "comma, ok" pattern on send statements
if ch, ok <- i; !ok {
break
}
}
}()
return ch
}

func fanIn(inputs ...<-chan int) <-chan int {
ch := make(chan int)

for _, i := range inputs {
i := i
go func() {
for n := range i {
// "comma, ok" pattern on send statements
if ch, ok <- n; !ok {
close(i) // invalid operation: close(i) (cannot close receive-only channel)
break
}
}
}()
}

return ch
}

Marvin Stenger

unread,
Mar 2, 2016, 5:42:26 PM3/2/16
to golang-nuts, nfi...@junctionbox.ca, s...@samwhited.com
"comma, ok" pattern on send statements is no option, looks way too odd and ugly.
Making the send statement a boolean expression would be the cleaner way to implement this idea

Tapio Raevaara

unread,
Mar 2, 2016, 5:42:41 PM3/2/16
to golang-nuts
 I'm talking specifically about send/receive *outside* of select. I though it was obvious that select would ignore those cases anyway, but I guess I should've made that even more obvious. :)


On Thursday, March 3, 2016 at 12:28:37 AM UTC+2, Dave Cheney wrote:
Have a look back in the git history to the point when send and receive used to work like this. There is lots of historical discussion, pre go 1.0, that explains why things are the way they are.

 Communication on nil channel *used* to panic? I've got to admit I've totally forgotten this...! Thanks for pointing this out.

Hm, need to do some digging... I'd still greatly appreciate any links to discussions or examples of the usefulness of the current behaviour.

Nico

unread,
Mar 2, 2016, 5:46:15 PM3/2/16
to golan...@googlegroups.com
On 02/03/16 22:42, Marvin Stenger wrote:
> "comma, ok" pattern on send statements is no option, looks way too odd and ugly.
> Making the send statement a boolean expression would be the cleaner way to implement this idea

Yes, I also like it better as a boolean expression:

https://play.golang.org/p/cfRv67pEzJ

package main

import "fmt"

func main() {
ch := fanIn(seq(5), seq(10))

for i := range ch {
fmt.Println(i)
if i > 5 {
break
}
}

close(ch) // invalid operation: close(ch) (cannot close receive-only channel)
}

func seq(n int) <-chan int {
ch := make(chan int)
go func() {
for i := 0; i < n; i++ {
// "comma, ok" pattern on send statements
if ok := ch <- i; !ok {
break
}
}
}()
return ch
}

func fanIn(inputs ...<-chan int) <-chan int {
ch := make(chan int)

for _, i := range inputs {
i := i
go func() {
for n := range i {
// "comma, ok" pattern on send statements
if ok := ch <- n; !ok {

Nigel Tao

unread,
Mar 2, 2016, 6:10:09 PM3/2/16
to Tapio Raevaara, golang-nuts
On Thu, Mar 3, 2016 at 9:42 AM, Tapio Raevaara <tapio.r...@iki.fi> wrote:
> On Thursday, March 3, 2016 at 12:28:37 AM UTC+2, Dave Cheney wrote:
>>
>> Have a look back in the git history to the point when send and receive
>> used to work like this. There is lots of historical discussion, pre go 1.0,
>> that explains why things are the way they are.
>
>
> Communication on nil channel *used* to panic? I've got to admit I've
> totally forgotten this...! Thanks for pointing this out.
>
> Hm, need to do some digging... I'd still greatly appreciate any links to
> discussions or examples of the usefulness of the current behaviour.

It's not so much that it's useful. It's that it's consistent (see e.g.
https://groups.google.com/forum/#!msg/golang-dev/vO9XVbNkC-Q/Hr5qBl7KwgQJ
and https://groups.google.com/forum/#!msg/golang-nuts/WrF9kBDhGEg/pLRYVMByMf0J).

https://groups.google.com/forum/#!msg/golang-dev/VK_L5x1EgBI/0y43UG94qqIJ
says that:

----
For example, given that a select
is defined to wait for one of a collection of cases to be ready
and then execute it, a single-case select:

select {
case xxx:
yyy
}

should, since it is waiting on only a single communication, behave
the same as running the case directly:

{
xxx
yyy
}

Losing that property would, in my opinion, be a serious mistake.
It would make communications behave differently inside select
and outside select, introducing special cases for programmers
to stumble across.

It would be like

switch x {
case y:
...
}

being different from

if x == y {
...
}
----

Dave Cheney

unread,
Mar 2, 2016, 6:17:47 PM3/2/16
to golang-nuts
Former: The language change happened in r57, back in 2011, https://golang.org/doc/devel/pre_go1.html#r57. The change is spread across multiple commits as it was done in two stages, https://codereview.appspot.com/3973051, incidentally this was the introduction and first use of gofix.

Nico

unread,
Mar 2, 2016, 6:42:35 PM3/2/16
to golan...@googlegroups.com
On 02/03/16 23:17, Dave Cheney wrote:
> Former: The language change happened in r57, back in 2011, https://golang.org/doc/devel/pre_go1.html#r57. The change is spread across multiple commits as it was done in two stages, https://codereview.appspot.com/3973051, incidentally this was the introduction and first use of gofix.
>
> Latter: http://dave.cheney.net/2014/03/19/channel-axioms

Dave, thanks for digging this out!

Christopher Brodt

unread,
Mar 2, 2016, 6:49:59 PM3/2/16
to golan...@googlegroups.com
It seems like lock contention is a major pain point with channels in Go. What would some potential solutions be? I don't suppose a lock-free implementation is feasible.

Tapio Raevaara

unread,
Mar 2, 2016, 6:56:35 PM3/2/16
to golang-nuts, tapio.r...@iki.fi

I can understand that point of view. Then again, according to the spec, those cases aren't exactly the *same* thing, even if they *behave* the same way right now. In the first case it's the *select* that blocks because no communication can happen, in the second one it's the communication operation itself that blocks... but I guess that difference might be a bit too subtle for some. :)

Nevertheless, I find it really ugly that nil channel ops simply block silently and permanently. Other blocking operations can be recovered from. Panics can be recovered from. Read from a nil channel... that goroutine is forever gone.
 

Dave Cheney

unread,
Mar 2, 2016, 7:04:13 PM3/2/16
to golang-nuts


On Thursday, 3 March 2016 10:49:59 UTC+11, Christopher Brodt wrote:
It seems like lock contention is a major pain point with channels in Go. What would some potential solutions be? I don't suppose a lock-free implementation is feasible.

This is not true. I encourage you to check out the benchmarks in the runtime package.

ch...@uberbrodt.net

unread,
Mar 2, 2016, 7:18:13 PM3/2/16
to golang-nuts
Dave, I will take a look at those benchmarks, thanks. I've been led to believe that there's a significant cost to using channels vs mutexes by the OP's article and the following:

http://jmoiron.net/blog/go-performance-tales/
http://bravenewgeek.com/go-is-unapologetically-flawed-heres-why-we-use-it/

Personally, I've not had an issue, though I'm a relatively recent Go programmer.

Dave Cheney

unread,
Mar 2, 2016, 7:47:04 PM3/2/16
to golang-nuts
Sending data to another goroutine will never be free, there is always some overhead. You should use channels to communicate meaningful amounts of work and consider if two operations are so tightly coupled that one cannot proceed without the other, is your problem actually sequential, rather than concurrent.

tsuna

unread,
Mar 2, 2016, 8:00:47 PM3/2/16
to Dave Cheney, golang-nuts
Maybe I’m blind but I didn’t find a clear explanation in any of those
links as to why sending to / receiving from a nil channel (outside a
select) was made to hang forever (and leak the calling goroutine)
rather than crash the program with a panic?

--
Benoit "tsuna" Sigoure

Devon H. O'Dell

unread,
Mar 2, 2016, 8:17:11 PM3/2/16
to Dave Cheney, golang-nuts
2016-03-02 16:04 GMT-08:00 Dave Cheney <da...@cheney.net>:
> On Thursday, 3 March 2016 10:49:59 UTC+11, Christopher Brodt wrote:
>>
>> It seems like lock contention is a major pain point with channels in Go.
>> What would some potential solutions be? I don't suppose a lock-free
>> implementation is feasible.
>
> This is not true. I encourage you to check out the benchmarks in the runtime
> package.

Lock-free implementations have been proposed a couple of times for
buffered channels, (design document[1], ML post[2]). This work was
merged[3] (along with work to reduce lock overhead in the channel
implementation[4]), so channel operations are lock-free in some cases.

With unbuffered channels, a lock-free implementation is infeasible.
While it may be possible, the additional latency imposed on
non-contended workloads would severely degrade throughput. The select
statement also complicates matters here.

[1]: https://docs.google.com/document/d/1yIAYmbvL3JxOKOjuCyon7JhW4cSv1wy5hC0ApeGMV9s/pub
[2]: https://groups.google.com/forum/#!topic/golang-dev/0IElw_BbTrk
[3]: https://codereview.appspot.com/110580043/
[4]: https://codereview.appspot.com/112990043/

Jesse McNelis

unread,
Mar 2, 2016, 9:16:03 PM3/2/16
to tsuna, Dave Cheney, golang-nuts

On 3 Mar 2016 12:00 p.m., "tsuna" <tsun...@gmail.com> wrote:
>
> Maybe I’m blind but I didn’t find a clear explanation in any of those
> links as to why sending to / receiving from a nil channel (outside a
> select) was made to hang forever (and leak the calling goroutine)
> rather than crash the program with a panic?
>

It's specifically about making the behaviour of channels orthogonal to the select feature. Channel operations act the same whether they are in a select.

The benefit of this is that changes to how channels work don't require special cases in how select works.

Select only cares about whether a channel operation can proceed. New versions of Go can add additional operations to channels and select will still work the same as long as those operations maintain the interface of being able to proceed or not.

Jason E. Aten

unread,
Mar 3, 2016, 2:59:19 AM3/3/16
to golang-nuts, tapio.r...@iki.fi
Review slides 24-27 of John Graham-Cummings presentation at Gohpercon 2014

http://www.slideshare.net/cloudflare/a-channel-compendium

Hint: if you are ever using a channel outside of a select in production code, you are probably doing it wrong. 

You risk deadlock if you try to gracefully shutdown that process.

Ian Davis

unread,
Mar 3, 2016, 8:51:17 AM3/3/16
to golan...@googlegroups.com
 
On Thu, Mar 3, 2016, at 07:59 AM, Jason E. Aten wrote:
Review slides 24-27 of John Graham-Cummings presentation at Gohpercon 2014
 
 
Hint: if you are ever using a channel outside of a select in production code, you are probably doing it wrong. 
 
You risk deadlock if you try to gracefully shutdown that process.
 
Are you suggesting that ranging over a channel is bad practice?
 
for item := range channel {
 
}
 
 
 

ch...@uberbrodt.net

unread,
Mar 3, 2016, 10:33:01 AM3/3/16
to golang-nuts
No, because range checks if the channel is closed. Equivalent for/select looks like this:

for {
    select {
        x <- someChan
            //do stuff
        <- someChan
            //channel closed, bail out of for loop
            return

Ian Davis

unread,
Mar 3, 2016, 10:39:37 AM3/3/16
to golan...@googlegroups.com
 
On Thu, Mar 3, 2016, at 03:33 PM, ch...@uberbrodt.net wrote:
No, because range checks if the channel is closed. Equivalent for/select looks like this:
 
I agree but I was responding to the suggestion that using channels outside a select block is somehow bad practice.
 
Ian

Jason E. Aten

unread,
Mar 3, 2016, 10:40:22 AM3/3/16
to Ian Davis, golang-nuts
A conditional yes: if you are writing a server process that needs to be able to shut down gracefully, then you should never range over a channel, because it can deadlock the shutdown. All your channel communication needs to be inside a select loop, where one of the options is receiving on the "shutdown request" channel.

--
You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/LM648yrPpck/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.

Ian Davis

unread,
Mar 3, 2016, 10:51:15 AM3/3/16
to Jason E. Aten, golang-nuts
On Thu, Mar 3, 2016, at 03:40 PM, Jason E. Aten wrote:
A conditional yes: if you are writing a server process that needs to be able to shut down gracefully, then you should never range over a channel, because it can deadlock the shutdown. All your channel communication needs to be inside a select loop, where one of the options is receiving on the "shutdown request" channel.
 
Forbidding the use of range here seems strange when there are so many other ways to deadlock a server process. It's easy enough to exit a range loop by use of a shared atomic bool or similar. I don't understand these kind of sweeping rules that forbid useful language constructs.
 
Ian

ch...@uberbrodt.net

unread,
Mar 3, 2016, 11:03:15 AM3/3/16
to golang-nuts, j.e....@gmail.com
On Thu, Mar 3, 2016, at 03:40 PM, Jason E. Aten wrote:
A conditional yes: if you are writing a server process that needs to be able to shut down gracefully, then you should never range over a channel, because it can deadlock the shutdown. All your channel communication needs to be inside a select loop, where one of the options is receiving on the "shutdown request" channel.

Well, yes, if you never close the channel range will not work for you like you expect. But if your "shutdown request" is closing the channel it should work fine.

Forbidding the use of range here seems strange when there are so many other ways to deadlock a server process. It's easy enough to exit a range loop by use of a shared atomic bool or similar. I don't understand these kind of sweeping rules that forbid useful language constructs.
 I think that using for/select would be more explicit and follow Go conventions, but your proposed approach should work as well.

Thomas Bushnell, BSG

unread,
Mar 3, 2016, 11:13:46 AM3/3/16
to Jason E. Aten, golang-nuts, tapio.r...@iki.fi
You should never write production code which requires a graceful shutdown.

--
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.

Jakob Borg

unread,
Mar 3, 2016, 11:37:16 AM3/3/16
to ch...@uberbrodt.net, golang-nuts
On 03 Mar 2016, at 22:33, ch...@uberbrodt.net wrote:
>
> No, because range checks if the channel is closed. Equivalent for/select looks like this:
>
> for {
> select {
> x <- someChan
> //do stuff
> <- someChan
> //channel closed, bail out of for loop
> return
> }
> }

What sorcery is this? Clearly both receive statements can proceed, so one should be chosen at random, no?

//jb

ch...@uberbrodt.net

unread,
Mar 3, 2016, 12:08:01 PM3/3/16
to golang-nuts, ch...@uberbrodt.net
What sorcery is this? Clearly both receive statements can proceed, so one should be chosen at random, no? 

Ah, you're right, that last case should be a "quitChan" to work properly. Hmm, maybe you do always need a select{}!

Axel Wagner

unread,
Mar 3, 2016, 1:02:53 PM3/3/16
to golang-nuts, ch...@uberbrodt.net
for {
    select {
        case v, ok := <-ch:
            if !ok {
                break
            }
            // ...

Axel Wagner

unread,
Mar 3, 2016, 1:04:44 PM3/3/16
to golang-nuts, j.e....@gmail.com, tapio.r...@iki.fi
Require -- agreed.
Offer -- strongly disagree.

It is completely fine (and a good thing), if a graceful shutdown can decrease recovering time. In that case an in-production upgrade is simpler and faster but if something crashes you can still recover without a hitch.

Caleb Spare

unread,
Mar 3, 2016, 1:34:11 PM3/3/16
to Axel Wagner, golang-nuts, Jason Aten, tapio.r...@iki.fi
One problem with that is that it's easy for "offered" to become "required" over time as the code changes without anyone noticing, because most shutdowns are clean. Then it crashes and you realize that it's in an unrecoverable state.

Axel Wagner

unread,
Mar 3, 2016, 2:37:50 PM3/3/16
to golang-nuts, axel.wa...@googlemail.com, j.e....@gmail.com, tapio.r...@iki.fi
In my (somewhat limited, agreed) experience, processes die for unexpected reasons *all the time*. And if they don't, there's always the chaos monkey to force them to to test those rarely exercised code paths.
Reply all
Reply to author
Forward
0 new messages