Panic: send on closed channel in a unit test

1,143 views
Skip to first unread message

ugol

unread,
Apr 13, 2016, 10:24:22 AM4/13/16
to golang-nuts
Hi Gophers,
I have a piece of code similar to this:
https://play.golang.org/p/OBhc-OysjT

(the code in the test() method is actually in a func TestSimpleId(t
*testing.T)).

The issue is that running a 'go test' sometimes I get a

panic: send on closed channel

which I can't really understand: looks like the thread is sending the
number on the channel after it's closed, but adding a pause before the
close doesn't help

for example:
id0 := MakeID(0)
x := <-id0
x = <-id0
x = <-id0
x = <-id0
if x != 3 {
t.Errorf("Wrong id, expected %d, was %d", 3, x)
}
time.Sleep(10 * time.Millisecond)
close(id0)

still gets the panic. There is definitely something I am missing on
how channels work... who is pushing stuff on the channel after it's
closed?

tia!
--
uL

Roberto Zanotto

unread,
Apr 13, 2016, 10:43:10 AM4/13/16
to golang-nuts
In the code you provided on the playground, function test is closing the channel,
shortly after that the goroutine created in MakeID (running concurrently!) is sending something on the closed channel, thus the panic.

The sender (producer) should close a channel, never the receiver (consumer).
The sender closes the channel to signal that he is done sending things.
In this case, the channel should be closed after the for loop in MakeID.
Or you can just avoid closing the channel, it's not mandatory, channels are garbage collected anyway.

Jordan Krage

unread,
Apr 13, 2016, 10:49:06 AM4/13/16
to golang-nuts
The goroutine that is sending on ch never stops sending. No matter how long you pause for, the goroutine is executing an infinite loop. You will have to end that goroutine's execution before closing the channel (whether you signal it, or limit the number of iterations, etc is up to you).

ugol

unread,
Apr 13, 2016, 11:06:36 AM4/13/16
to Jordan Krage, golang-nuts
Yep, I know that the goroutine is always running, but it won't push on
the channel if not "asked for" with x = <-id0.

I can solve it easily either without explicitly closing it or
signaling to exit with another channel, but I still don't understand
when the goroutine is pushing the value
> --
> 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.



--
uL

Pragmatist
http://blog.ugolandini.com
http://www.flickr.com/photos/ugol/

Roberto Zanotto

unread,
Apr 13, 2016, 11:09:47 AM4/13/16
to golang-nuts, jma...@gmail.com
The sender is blocked trying to send a value (because the receiver is not receiving anymore).
Then the receiver closes the channel and at that point the sender is unblocked with a panic.

Jordan Krage

unread,
Apr 13, 2016, 11:11:38 AM4/13/16
to golang-nuts, jma...@gmail.com
> Yep, I know that the goroutine is always running, but it won't push on 
> the channel if not "asked for" with x = <-id0. 

Attempting to send on a closed channel will always panic, regardless of whether anything is receiving on the other end, or whether that channel is buffered or not.

John McKown

unread,
Apr 13, 2016, 11:28:44 AM4/13/16
to golan...@googlegroups.com
On Wed, Apr 13, 2016 at 10:06 AM, ugol <ugo.l...@gmail.com> wrote:
Yep, I know that the goroutine is always running, but it won't push on
the channel if not "asked for" with x = <-id0.

​Not really true. The goroutine has generated the value and is blocked while waiting to _complete_ sending it on the channel. This assumes an unbuffered channel. The assignment reads the value and the goroutine unblocks to generate the next one. The goroutine is __in the processing of sending__ when the channel is closed, so the send aborts and the goroutine panics.
 

I can solve it easily either without explicitly closing it or
signaling to exit with another channel, but I still don't understand
when the goroutine is pushing the value


​What it really appears, to me, that you want is a often called a "generator function". If so, perhaps this will help:
I rather like one of the examples in
<quote>

Using channels to emulate Python generators kind of works, but they introduce concurrency where none is needed, and it adds more complication than's probably needed. Here, just keeping the state explicitly is easier to understand, shorter, and almost certainly more efficient. It makes all your questions about buffer sizes and garbage collection moot.

type fibState struct {
    x, y int
}

func (f *fibState) Pop() int {
    result := f.x
    f.x, f.y = f.y, f.x + f.y
    return result
}

func main() {
    fs := &fibState{1, 1}
    for i := 0; i < 10; i++ {
        fmt.Println(fs.Pop())
    }
}

</quote>​


--
How many surrealists does it take to screw in a lightbulb? One to hold the griffon and one to fill the bathtub with brightly colored LEDs.

Maranatha! <><
John McKown

ugol

unread,
Apr 13, 2016, 12:53:26 PM4/13/16
to John McKown, golang-nuts

Hi John

​Not really true. The goroutine has generated the value and is blocked while waiting to _complete_ sending it on the channel. This assumes an unbuffered channel. The assignment reads the value and the goroutine unblocks to generate the next one. The goroutine is __in the processing of sending__ when the channel is closed, so the send aborts and the goroutine panics.

thx, now it's clear why it wasn't working :)
 
​What it really appears, to me, that you want is a often called a "generator function". If so, perhaps this will help:

exactly, this is a generator for message ids (it's part of a client library for Infinispan). 
In this case concurrent access is needed, because you can have different clients (goroutines) connecting, so this particular example is not a good fit, but the link about generators confirms that the correct approach is to have another channel to close "server-side"
 
thx!
--
uL
Reply all
Reply to author
Forward
0 new messages