method to detect write on closed channel

4,912 views
Skip to first unread message

Paul Borman

unread,
Feb 18, 2012, 6:36:08 PM2/18/12
to golang-nuts
I have a system that registers for events by passing in what kind of event to register for and a channel  to send the caught events on.  When I am done with the registration I could call an "unregister" function but I think it would be more straight forward to simply close the channel I am no longer interested in receiving data on.  This is the dreaded "reader closing the channel".  Of course, the next write will panic.  Here is a simple solution:

type T bool

// Send returns true if it was able to send t on channel c.
// It returns false if c is closed.
func Send(c chan T, t T) (ok bool) {
defer func() {recover()}()
c <- t
return true
}

I believe this is safe because the only possible panic is a write on a closed channel. Is there any reason this would not be safe?

I did look through the archives and saw one post a year ago discussing recovering the panic on a write but it was in a much larger context (and so it had to see what kind of panic it was).

Thanks,

    -Paul

Krzysztof Kowalik

unread,
Feb 18, 2012, 6:59:34 PM2/18/12
to Paul Borman, golang-nuts
Yeap, it is safe. It gonna panic only in case where all goroutines are asleep. 

Cheers, nu7

2012/2/18 Paul Borman <bor...@google.com>

Patrick Mylund Nielsen

unread,
Feb 18, 2012, 7:05:11 PM2/18/12
to Paul Borman, golang-nuts
If you want to return an error, you can do:

func Send(c chan T, t T) error {
var err error
defer func() {
if x := recover(); x != nil {
err = fmt.Errorf("Unable to send: %v", x)
}
}
c <- t
return err

Patrick Mylund Nielsen

unread,
Feb 18, 2012, 7:18:24 PM2/18/12
to Paul Borman, golang-nuts
Sorry, that's:

func Send(c chan T, t T) (err error) {


defer func() {
if x := recover(); x != nil {
err = fmt.Errorf("Unable to send: %v", x)
}
}
c <- t
return
}

Russ Cox

unread,
Feb 18, 2012, 8:19:41 PM2/18/12
to Paul Borman, golang-nuts
On Sat, Feb 18, 2012 at 18:36, Paul Borman <bor...@google.com> wrote:
> I have a system that registers for events by passing in what kind of event
> to register for and a channel  to send the caught events on.  When I am done
> with the registration I could call an "unregister" function but I think it
> would be more straight forward to simply close the channel I am no longer
> interested in receiving data on.  This is the dreaded "reader closing the
> channel".  Of course, the next write will panic.  Here is a simple solution:

This is like writing

func Index(a []string, i int) (v string, ok bool) {
defer func() { recover() }()
v = a[i]
ok = true
return
}

Yes, it works, but it is an error-prone way to program.
It would be better not to send to closed channels.

Russ

Paul Borman

unread,
Feb 19, 2012, 5:53:54 PM2/19/12
to r...@golang.org, golang-nuts
I agree in general and would not recommend this as the normal course of events.  The purpose here was to eliminate locking or synchronization logic needed for the one who created the channel (the reader) and later wants to indicate they do not need further events.  I could send an unregister event to the server (the only writer to the channel) so it can find and close the channel but that requires quite a bit more logic.

Fortunately this is for a personal project :-)

I am not sure I agree with with the error-prone comment in this particular instance, but mostly I wanted to make sure I wasn't missing something.

Thanks,

    -Paul

Kyle Lemons

unread,
Feb 20, 2012, 4:32:34 AM2/20/12
to Paul Borman, r...@golang.org, golang-nuts
I keep meaning to polish up my code and its related blog post about this, but the way I solve it is having, essentially, a "reader-closeable" channel.  It's a data structure containing two channels, of course: one that's the usual channel, and one that's a "chan closed struct{}".  Sends select between both channels, so if it's closed it ends (you can even put something there to perform unregistration so it doesn't attempt the send again).  It even behaves nicely in cases where lots of senders are blocking on send and the reader closes, because it immediately unblocks all of the senders.  No panicing, and the logic reads quite nicely.  It has more overhead, of course, than simply doing the equivalent mutex+bool "isClosed" but I think the channel semantics work really nicely with the other constructs involved.

Paul Borman

unread,
Jun 1, 2015, 9:52:48 AM6/1/15
to Hongcai Deng, golang-nuts, Russ Cox
What data race is that?  While I have not had issues the few times I have done this, I do not advocate catching the panic generated while writing on a closed channel.

    -Paul

On Mon, Jun 1, 2015 at 4:20 AM, Hongcai Deng <dengh...@gmail.com> wrote:
Your way seems has a data race using -race, in production, you never meet a bug?

Hongcai Deng

unread,
Jun 1, 2015, 10:52:17 AM6/1/15
to golan...@googlegroups.com, bor...@google.com, r...@golang.org
Your way seems has a data race using -race, in production, you never meet a bug?

On Monday, February 20, 2012 at 5:32:34 PM UTC+8, Kyle Lemons wrote:
Reply all
Reply to author
Forward
0 new messages