Go talks io12: Go Concurrency Patterns: garbage collection

3,672 views
Skip to first unread message

Peter Kleiweg

unread,
Jul 2, 2012, 3:07:42 PM7/2/12
to golan...@googlegroups.com

After watching the talk Go Concurrency Patterns (
http://www.youtube.com/watch?v=f6kdp27TYZs ) I am left
wandering, exactly what happens with a go routine that is
blocked on writing to a channel, and there are no longer any go
routines left reading from that channel. If there are no other
references to the channel left, than the blocked go routine gets
garbage collected. Did I hear that right? Does this happen
generally, or only in the case of time.After()?

The last example presented was with several go routines doing a
search, only some of them used, and then the program exits, and
the searches that are still running are just aborted.

But if the program doesn't terminate, those searches keep on
going, until they try to write their results to the channel.

So what I would like to know is: when do you concern yourself
with cleaning up go routines, and what is the way to do the
clean up ("pattern"-wise); and when can you just ignore the
issue?



--
Peter Kleiweg
my Go programming cookbook: http://www.let.rug.nl/~kleiweg/go/

Rob 'Commander' Pike

unread,
Jul 2, 2012, 3:15:14 PM7/2/12
to Peter Kleiweg, golan...@googlegroups.com
To keep the talk simple, I deliberately avoided all these issues. They are in fact a suitable topic for a second talk.

Goroutines are not garbage-collected. They must return from the top-level function (or panic) to have all their resources recovered.

Channels persist as long as one reference to them persists, since without difficult (and in general impossible) analysis it's hard to prove a second reference won't arise.

For the case of time.After, the implementation uses a buffered channel. The goroutine's send is therefore guaranteed to be able to complete, so the goroutine and the channel can be garbage-collected. Buffering can also be used to address resource management issues in the Google example at the end of the talk. A well-written program would arrange, using code that probably wouldn't fit on one slide, to use a channel to cancel the outstanding requests.

-rob

Shane Hansen

unread,
Jul 2, 2012, 4:14:35 PM7/2/12
to Peter Kleiweg, golan...@googlegroups.com
Is this a good example of the situation you describe?

I tried to make this example slightly non-trivial. There is initially a reader
on the channel, but after the first read the reader stops reading, creating a situation
where the writer could be blocked indefinitely on that channel.

The go runtime detects the deadlock and barfs (technical term).

Thanks,
--Shane

David Symonds

unread,
Jul 2, 2012, 5:10:38 PM7/2/12
to Shane Hansen, Peter Kleiweg, golan...@googlegroups.com
On Mon, Jul 2, 2012 at 1:14 PM, Shane Hansen <shanem...@gmail.com> wrote:

> Is this a good example of the situation you describe?
> http://play.golang.org/p/3WYZSAhGZo

Try again. You need to click "share" *after* you've made code changes.

Shane Hansen

unread,
Jul 2, 2012, 5:12:22 PM7/2/12
to David Symonds, Peter Kleiweg, golan...@googlegroups.com

David Symonds

unread,
Jul 2, 2012, 5:14:37 PM7/2/12
to Shane Hansen, Peter Kleiweg, golan...@googlegroups.com
On Mon, Jul 2, 2012 at 2:12 PM, Shane Hansen <shanem...@gmail.com> wrote:

> My bad. Thank you.
> http://play.golang.org/p/3WYZSAhGZo

Try again. Make sure you are copying the URL from the box that pops
open, not from your browser's address bar.

Shane Hansen

unread,
Jul 2, 2012, 5:25:11 PM7/2/12
to David Symonds, Peter Kleiweg, golan...@googlegroups.com
Wow, kind of embarrassing. I appreciate your patience.
I'm about 90% sure this link leads to an example of a writer blocked on
a channel that will no longer be read from.

Rob 'Commander' Pike

unread,
Jul 2, 2012, 5:33:09 PM7/2/12
to Shane Hansen, David Symonds, Peter Kleiweg, golan...@googlegroups.com
Yes. The program has a bug.

-rob

Aaron France

unread,
Jul 2, 2012, 5:33:20 PM7/2/12
to golan...@googlegroups.com
This talk really was interesting. Thanks for doing it.

Regards,
Aaron

Cole Mickens

unread,
Jul 2, 2012, 6:06:36 PM7/2/12
to golan...@googlegroups.com, David Symonds, Peter Kleiweg
Is this the more interesting case in question? http://play.golang.org/p/uE-w7HG_2p

I guess I can imagine three ways of allowing gc of the goroutine:
  1. A select with a write case and a default case if they are unsure that they will exhaustively read from the chan.
  2. A time.After timeout case that will eventually execute and allow the function to return.
  3. Another quit channel allowing a "manual" exit of the gorouting
This doesn't work on the playground (in fact, parts of it don't work even without the time functionality in use, I'm not sure why, but this, I believe, demonstrates some of the ways to cleanup:

The output on my machine:
c1 0
fun2 returning
c2 0
c3 0
c4 0
fun4 returning
fun3 returning
done

On Monday, July 2, 2012 2:25:11 PM UTC-7, Shane Hansen wrote:
Wow, kind of embarrassing. I appreciate your patience.
I'm about 90% sure this link leads to an example of a writer blocked on
a channel that will no longer be read from.
http://play.golang.org/p/m_2FAIiW1o

On Mon, Jul 2, 2012 at 3:14 PM, David Symonds <ds...@golang.org> wrote:

Maxim Khitrov

unread,
Jul 2, 2012, 6:37:11 PM7/2/12
to Rob 'Commander' Pike, golan...@googlegroups.com
Rob,

Two of the questions you were asked had to do with testing concurrent
code and garbage collection of goroutines. Your answer to the former
was to show that the user of boring() doesn't actually need to know
how that function is implemented. It simply returns a channel, which
you use to receive values. The fact that there is a goroutine in the
background doing some work isn't relevant. (Please correct me if that
summary isn't accurate.)

The problem I see with this is that without garbage collection for
permanently blocked goroutines, and with no way for the caller of
boring() to close the channel or indicate in some other fashion that
no further values are needed, this becomes a recipe for memory leaks.
Your example worked because the goroutine died when main() was
finished, but what if this was a long-running daemon process?

I've run into this problem several times now while playing with Go. I
have a receiver that reads a few values from a channel and, at some
point, decides that no additional values are needed. Think along the
lines of generators in Python - keep feeding me values until I have
what I need. As far as I can tell, there is no way to implement this
pattern with your simple use of boring(). You have to move to your
other example, which includes a 'quit' channel. I don't like this
solution, because now you're getting into the details of how boring()
might be implemented.

For example, what happens if boring() launches multiple goroutines
that all send values to c? Should you now have a separate quit channel
for each of those goroutines? Should you send the right number of quit
notifications on a single channel (you don't know how many goroutines
there are)? Should you use the close() broadcast trick? I think there
needs to be a clean way for the receiver to say "I'm done receiving
from c; all goroutines that are blocked on a send to c, and any values
that are already buffered, can now be garbage collected."

What are your thoughts on this?

- Max

Paul Borman

unread,
Jul 2, 2012, 6:56:16 PM7/2/12
to Maxim Khitrov, Rob 'Commander' Pike, golan...@googlegroups.com
You can use a single quit channel for all related producers.  The consumer then simply closes the quit channel when it is done and all pending and future reads fo the quit channel will return with ok set to false.  So:

    select {
    case <- quit:
        return
    case out <- nextValue:
    }

    -Paul

Rob 'Commander' Pike

unread,
Jul 2, 2012, 7:33:02 PM7/2/12
to Paul Borman, Maxim Khitrov, golan...@googlegroups.com
As Paul says, you can use close(quit). You can also daisy-chain the quit channel, as suggested in my talk.

-rob

David Leimbach

unread,
Jul 2, 2012, 10:25:40 PM7/2/12
to golan...@googlegroups.com, Maxim Khitrov, Rob 'Commander' Pike
Are you sure you want a channel consumer to close the channel?  I was always of the impression that close should be used on what is usually the write side of channel (yes you can use channels both ways IIRC, but many people "type" their channels as receive only or send only)

Dave

Maxim Khitrov

unread,
Jul 2, 2012, 11:08:09 PM7/2/12
to David Leimbach, golan...@googlegroups.com, Rob 'Commander' Pike
Two different channels. Paul and Rob suggest creating a separate
channel called 'quit' that is closed by the receiver as a way of
asking the sender(s) to terminate. I don't really like this method (or
rather I really don't like it), because it increases the complexity of
the code for no apparent benefit.

IMHO, a better solution would be to define the semantics of calling
close() on a receive-only channel, and adding "comma ok" send form to
let the senders determine whether any receive ends of a channel are
still open. Obviously, these changes will not be in Go1, but I think
it is worth revisiting this topic for Go2.

- Max

Jesse McNelis

unread,
Jul 2, 2012, 11:13:53 PM7/2/12
to Maxim Khitrov, golan...@googlegroups.com
On Tue, Jul 3, 2012 at 1:08 PM, Maxim Khitrov <m...@mxcrypt.com> wrote:
> IMHO, a better solution would be to define the semantics of calling
> close() on a receive-only channel, and adding "comma ok" send form to
> let the senders determine whether any receive ends of a channel are
> still open.

What does the sender do with the value it just generated?
What if the generation had side effects?
You'd need to know the internal implementation to know if it's safe
for a receiver to close a channel.
This has been discussed and it's a bad idea.

Goroutines and channels aren't the same as python generators,
they can't be because they need to handle concurrency.

If a producer doesn't explicitly give you a way to stop itself, then
you have to read from the channel until it closes it because it needs you too.


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

Maxim Khitrov

unread,
Jul 2, 2012, 11:53:32 PM7/2/12
to Jesse McNelis, golan...@googlegroups.com
On Mon, Jul 2, 2012 at 11:13 PM, Jesse McNelis <jes...@jessta.id.au> wrote:
> On Tue, Jul 3, 2012 at 1:08 PM, Maxim Khitrov <m...@mxcrypt.com> wrote:
>> IMHO, a better solution would be to define the semantics of calling
>> close() on a receive-only channel, and adding "comma ok" send form to
>> let the senders determine whether any receive ends of a channel are
>> still open.
>
> What does the sender do with the value it just generated?
> What if the generation had side effects?
> You'd need to know the internal implementation to know if it's safe
> for a receiver to close a channel.
> This has been discussed and it's a bad idea.

I see what you mean, but in the current implementation the sender just
becomes permanently blocked. You have a memory leak, which is going to
surprise many newcomers to Go (as evidenced by the number of questions
related to garbage collection of goroutines). I'm not saying that my
method is ideal, but in many simple cases it would result in cleaner
and clearer code.

You have to admit - close(quit) is a rather cryptic way of saying "I'm
no longer interested in channel c."

> Goroutines and channels aren't the same as python generators,
> they can't be because they need to handle concurrency.

Channels are more powerful than generators in Python, but I don't see
any theoretical reason why they can't be used in the same manner. The
problem is in figuring out when a goroutine becomes permanently
blocked. An explicit close() from the receiver would have made this
easier to solve.

> If a producer doesn't explicitly give you a way to stop itself, then
> you have to read from the channel until it closes it because it needs you too.

I understand this, but I think this behavior is going to surprise
people. If I have a goroutine generating the Fibonacci sequence to
infinity, it's not clear why there needs to be some explicit stop
indication in a garbage-collected language.

- Max

Andrew Gerrand

unread,
Jul 3, 2012, 12:00:46 AM7/3/12
to Maxim Khitrov, Jesse McNelis, golan...@googlegroups.com
On 2 July 2012 20:53, Maxim Khitrov <m...@mxcrypt.com> wrote:
> If a producer doesn't explicitly give you a way to stop itself, then
> you have to read from the channel until it closes it because it needs you too.

I understand this, but I think this behavior is going to surprise
people. If I have a goroutine generating the Fibonacci sequence to
infinity, it's not clear why there needs to be some explicit stop
indication in a garbage-collected language.

One good reason is that were goroutines garbage collected it would be very hard to diagnose a deadlock, as most of the state would be garbage collected before you get a stack trace.

Andrew  

David Leimbach

unread,
Jul 3, 2012, 12:10:15 AM7/3/12
to golan...@googlegroups.com, Maxim Khitrov
So how does one handle timeouts perfectly cleanly?  It's a bit important for services to be careful of resource consumption.

Timeouts are race conditions on purpose.  So cleaning them up can be a bit nasty.  Often some way of canceling a request will be needed so that resources can be reclaimed or outstanding items can be completed ASAP.

Dave



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

Jesse McNelis

unread,
Jul 3, 2012, 12:14:19 AM7/3/12
to Maxim Khitrov, golan...@googlegroups.com
On Tue, Jul 3, 2012 at 1:53 PM, Maxim Khitrov <m...@mxcrypt.com> wrote:
> Channels are more powerful than generators in Python, but I don't see
> any theoretical reason why they can't be used in the same manner.

Generators create values when you ask for them.
Goroutines produce values before you ask for them.

This distinction requires the producer to be in control of when to stop.


> I understand this, but I think this behavior is going to surprise
> people.

The surprise is because most programmers haven't used something like
goroutines and channels.
The closest thing they have used are generators. They get confused
when they try to use them as generators and they don't work the same.

> If I have a goroutine generating the Fibonacci sequence to
> infinity, it's not clear why there needs to be some explicit stop
> indication in a garbage-collected language.

If might be better to use a generator for this. This can be easily
implemented in Go.


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

David Leimbach

unread,
Jul 3, 2012, 12:22:02 AM7/3/12
to golan...@googlegroups.com, Jesse McNelis

 
You have to admit - close(quit) is a rather cryptic way of saying "I'm
no longer interested in channel c."

Why?  That's how I tell the OS I don't care about a file handle anymore. Would you like it to be called chanclose()?

David Leimbach

unread,
Jul 3, 2012, 12:29:07 AM7/3/12
to golan...@googlegroups.com, Maxim Khitrov

So how does one handle timeouts perfectly cleanly?  It's a bit important for services to be careful of resource consumption.


time.After works on a buffered channel OOPS!  So there's no issue here that I'm aware of

I don't know how I got confused here.  But godoc -src is pretty awesome :-)
 
Dave

Kyle Lemons

unread,
Jul 3, 2012, 3:41:10 AM7/3/12
to Maxim Khitrov, Jesse McNelis, golan...@googlegroups.com
On Mon, Jul 2, 2012 at 8:53 PM, Maxim Khitrov <m...@mxcrypt.com> wrote:
On Mon, Jul 2, 2012 at 11:13 PM, Jesse McNelis <jes...@jessta.id.au> wrote:
> On Tue, Jul 3, 2012 at 1:08 PM, Maxim Khitrov <m...@mxcrypt.com> wrote:
>> IMHO, a better solution would be to define the semantics of calling
>> close() on a receive-only channel, and adding "comma ok" send form to
>> let the senders determine whether any receive ends of a channel are
>> still open.
>
> What does the sender do with the value it just generated?
> What if the generation had side effects?
> You'd need to know the internal implementation to know if it's safe
>  for a receiver to close a channel.
> This has been discussed and it's a bad idea.

I see what you mean, but in the current implementation the sender just
becomes permanently blocked. You have a memory leak, which is going to
surprise many newcomers to Go (as evidenced by the number of questions
related to garbage collection of goroutines). I'm not saying that my
method is ideal, but in many simple cases it would result in cleaner
and clearer code.

You have to admit - close(quit) is a rather cryptic way of saying "I'm
no longer interested in channel c."

What it comes down to is this: closing a channel is a communication operation.  Channels are one-way pipes.  Either you complicate the implementation (and constrain future implementations and optimizations) by allowing this one particular case of two-way communication for an uncommon case (bad) or you let programmers account for it when necessary with a secondary channel (good).

If you don't like the way close(quit) reads, then make it close(ready) or wrap the two channels into a type with some methods that read the way you like.

Albert Strasheim

unread,
Jul 3, 2012, 3:56:00 AM7/3/12
to golan...@googlegroups.com, Peter Kleiweg
Hello


On Monday, July 2, 2012 9:15:14 PM UTC+2, Rob 'Commander' Pike wrote:
To keep the talk simple, I deliberately avoided all these issues. They are in fact a suitable topic for a second talk.

Thanks, great talk.

For a follow-up, we would really be interested to hear how you guys envision that one should structure these programs with thousands or millions of goroutines. I've looked at things like Vitess, but it doesn't seem like a good example "big software with goroutine nests". :-)

Random questions:

Is it mostly a big bunch of packages?

What does main look like?

What functions does main call? I imagine main is some kind of for { select {} } maybe.

If those functions spawn lots of goroutines, do you pass quit channels around everywhere to bring it all back down?

Does teardown of nests of goroutines in unit tests (in between test functions) work the same way?

Do you unit test code that spawns lots of goroutines or do you just hit it really hard at a system level?

How do you make the program exit gracefully with a signal? A goroutine that reads the signal channel and closes a bunch of quit channels?

How do you make sure you don't accidentally leak goroutines in some code paths? Tests that check runtime.NumGoRoutine after making everything quit? This becomes scarier when you have long running services instead of programs that run for a few seconds in response to a request and then exit.

In our project  we've taken a very Erlangy actor approach to our design so far (with lots of unit tests for the "logic" of the actors), but I don't really feel that we are testing our goroutines interactions very well at a unit and subsystem level. Which leads to bugs that like what you also see in the standard library:


Anyway, looking forward to the next video!

Regards

Albert

Tonic Artos

unread,
Jul 3, 2012, 5:07:25 AM7/3/12
to Albert Strasheim, golan...@googlegroups.com, Peter Kleiweg
It'd be nice if when attempting to write to a unbuffered channel, or one with a full buffer, the program would panic if there are no other references to the channel.

Peter Kleiweg

unread,
Jul 3, 2012, 5:51:07 AM7/3/12
to golang-nuts, pkle...@xs4all.nl
I wrote a library that provides a Python-like iterator using a
channnel.

When the iterator has sent its last thing, it cleans up and the
function ends. It works like this:

thinger := foo.NewThinger()
iterator := thinger.GetAllThings()
for thing := range iterator.Things() {
doSomethingWith(thing)
}
// iterator has cleaned-up and ended


When you want to exit the loop prematurely, you do it like this:

thinger := foo.NewThinger()
iterator := thinger.GetAllThings()
for thing := range iterator.Things() {
doSomethingWith(thing)
iterator.Break() // clean-up iterator and end
}

All iterators that are created are registered in the thinger object.
Calling Close() on this object cleans up all resources, including all
iterators that are still running:

thinger := foo.NewThinger()
iterator := thinger.GetAllThings()
for thing := range iterator.Things() {
doSomethingWith(thing)
break
}
// iterator still running
thinger.Close()
// iterator stopped and cleaned-up + thinger clean-up


Here is the implementation:

https://github.com/rug-compling/alpinocorpus-go/blob/master/alpinocorpus/reader.go

It all seems to work in this case, but I wonder how generally you
could apply this as a pattern, and if there is a better, simpler,
safer way.

Albert Strasheim

unread,
Jul 3, 2012, 5:57:59 AM7/3/12
to golan...@googlegroups.com
Another question:

For composing concurrent systems, is it better to have "objects" (structs with functions or goroutines+channels) that expose "normal" interface (functions you can call) or "channel-based" interfaces (channels you can send to)?

The standard library seems to have both:

time.Ticker composes nicely with other code that does channel things.

On the other side, with net Conns (in more complex peer-to-peer server type code, not straight-line client code), it seems one always has to wrap your Conns in some mix of reader/writer goroutines and channels. And closing when you want to shut down or even there is an error becomes interesting.

To some extent, it's like time.Ticker comes with "concurrent batteries included", but net doesn't, leaving you to build your own (sometimes poorly).

Any thoughts on when people writing servers (built on top of large collections of packages) should opt for one or the other?

Regards

Albert

Job van der Zwan

unread,
Jul 3, 2012, 8:51:18 AM7/3/12
to golan...@googlegroups.com, Jesse McNelis
On Tuesday, 3 July 2012 05:08:09 UTC+2, Maxim Khitrov wrote:
Two different channels. Paul and Rob suggest creating a separate 
channel called 'quit' that is closed by the receiver as a way of 
asking the sender(s) to terminate. I don't really like this method (or 
rather I really don't like it), because it increases the complexity of 
the code for no apparent benefit
On Tuesday, 3 July 2012 05:53:32 UTC+2, Maxim Khitrov wrote:
If I have a goroutine generating the Fibonacci sequence to 
infinity, it's not clear why there needs to be some explicit stop 
indication in a garbage-collected language. 

If you're sending off a goroutine on what basically is an infinite loop (most likely with channels for input and/or output), then it obviously needs an outside signal to terminate, agreed? As far as I can see, that's just inevitable programming logic, so how does this method explicitly doing that make the situation more complex than it inherently is? How does it have anything to do with garbage collection?

The explicitness reduces the number of surprises, at least to me. I literally don't know what the words lock, mutex or atomic means in the context of concurrent/parallel programming - to me those are just words that I probably should read into at some point. But I (think I) get goroutines, and I wouldn't be surprised if by using goroutines I have already accidentally implemented something "mutex-like" or "lock-like".

"Oh yeah, Fibonnacci sequence with goroutines... So I just create what's basically an infinite loop generating Fibonacci numbers, and it sends the result over this channel every iteration of the loop. Then I read out from that channel on the other side. I guess I have to add a way for that infinite loop to stop from the outside. Ah, you know what, I'll just use another channel, let's call it 'quit', and the select statement for that. Or maybe I'll make the 'result' channel a buffered channel, and add a 'request' channel that tells it how many Fibonacci numbers I want it to generate, or continue generating. Then sending a negative number could signal that I want it to terminate."

The point is that goroutines are just a bunch of sequential processes that don't wait for each other, except at the point of communication, which happens over channels. This can then be fine-tuned by things like the select statement, and making buffered channels. Super-easy to reason about.

Also, this is a good time as any to remind people of Gustavo's Tomb package, which is specifically made for controlling and monitoring the state (alive/dying/dead) of goroutines:

Job van der Zwan

unread,
Jul 3, 2012, 8:52:34 AM7/3/12
to golan...@googlegroups.com, Jesse McNelis
On Tuesday, 3 July 2012 05:08:09 UTC+2, Maxim Khitrov wrote:
Two different channels. Paul and Rob suggest creating a separate 
channel called 'quit' that is closed by the receiver as a way of 
asking the sender(s) to terminate. I don't really like this method (or 
rather I really don't like it), because it increases the complexity of 
the code for no apparent benefit
On Tuesday, 3 July 2012 05:53:32 UTC+2, Maxim Khitrov wrote:
If I have a goroutine generating the Fibonacci sequence to 
infinity, it's not clear why there needs to be some explicit stop 
indication in a garbage-collected language. 

Devon H. O'Dell

unread,
Jul 3, 2012, 9:28:31 AM7/3/12
to David Leimbach, golan...@googlegroups.com, Jesse McNelis
2012/7/3 David Leimbach <lei...@gmail.com>:
I think the confusion is less around close and more around closing
channel ``quit'' to signify that you are done with channel ``c.'' Like
closing one file descriptor to signify that you are done with a
different one.

--dho

David Leimbach

unread,
Jul 3, 2012, 11:38:43 AM7/3/12
to golan...@googlegroups.com, Maxim Khitrov, Jesse McNelis


On Tuesday, July 3, 2012 12:41:10 AM UTC-7, Kyle Lemons wrote:
On Mon, Jul 2, 2012 at 8:53 PM, Maxim Khitrov <m...@mxcrypt.com> wrote:
On Mon, Jul 2, 2012 at 11:13 PM, Jesse McNelis <jes...@jessta.id.au> wrote:
> On Tue, Jul 3, 2012 at 1:08 PM, Maxim Khitrov <m...@mxcrypt.com> wrote:
>> IMHO, a better solution would be to define the semantics of calling
>> close() on a receive-only channel, and adding "comma ok" send form to
>> let the senders determine whether any receive ends of a channel are
>> still open.
>
> What does the sender do with the value it just generated?
> What if the generation had side effects?
> You'd need to know the internal implementation to know if it's safe
>  for a receiver to close a channel.
> This has been discussed and it's a bad idea.

I see what you mean, but in the current implementation the sender just
becomes permanently blocked. You have a memory leak, which is going to
surprise many newcomers to Go (as evidenced by the number of questions
related to garbage collection of goroutines). I'm not saying that my
method is ideal, but in many simple cases it would result in cleaner
and clearer code.

You have to admit - close(quit) is a rather cryptic way of saying "I'm
no longer interested in channel c."

What it comes down to is this: closing a channel is a communication operation.  Channels are one-way pipes.  Either you complicate the implementation (and constrain future implementations and optimizations) by allowing this one particular case of two-way communication for an uncommon case (bad) or you let programmers account for it when necessary with a secondary channel (good).

Channels are not one-way pipes.  You can restrict their use as such by passing them to functions with types that indicate a direction, but you can certainly send and receive on a channel.


David Leimbach

unread,
Jul 3, 2012, 11:42:04 AM7/3/12
to golan...@googlegroups.com, David Leimbach, Jesse McNelis
Maybe, but that's not what was said.  :-)

I agree needing a pair of channels to handle the shutdown on one channel seems a bit unfortunate.  It's considered a bad idea to close the receive end of a channel, yet channels are bidirectional.

Some consistency is good and best practices needed is the take away I have from this conversation.
 
Dave

--dho

Paul Borman

unread,
Jul 3, 2012, 11:58:17 AM7/3/12
to David Leimbach, golan...@googlegroups.com, Jesse McNelis
It is possible to use close on the receive side of a channel, but as Kyle has mentioned, it is strongly discouraged.  Your writers basically need to do something like:  

        const cce = "runtime error: send on closed channel"  
        err := func() (err error) {
                defer func() {
                        p := recover()
                        if p == nil {
                                return
                        }
                        e, ok := p.(error)
                        if !ok || e.Error() != cce {
                                panic(p)
                        }
                        err = errors.New("send on close channel")
                }()
                ch <- value
                return nil
        }()

It is not very pretty, but it does work.

    -Paul
Reply all
Reply to author
Forward
0 new messages