Close of UDP socket doesn't unblock read in other goroutine

352 views
Skip to first unread message

Albert Strasheim

unread,
Jul 29, 2011, 11:31:52 AM7/29/11
to golang-nuts
Hello all

We have the following code that creates a UDP socket in the main
goroutine, reads it in another goroutine and then closes it in the
main goroutine. We find that the close does not wake up the reading
goroutine.

Is this right? It seems to differ from our experiences with TCP and
UNIX sockets.

package udp

import (
"testing"
"net"
"time"
"fmt"
"sync"
)

const mcast = false
var wg sync.WaitGroup

func TestUDP(t *testing.T){
mca, err := net.ResolveUDPAddr("udp4", "224.0.0.254:5000")
if err != nil {

panic(err)
}

fmt.Println("Listening")
udpl, err := net.ListenUDP("udp4", mca)
if err != nil {
panic(err)
}

// WORKAROUND
if err := udpl.SetReadTimeout(5e9); err != nil {
panic(err)
}

if mcast {
fmt.Println("JoinGroup")
if err = udpl.JoinGroup(mca.IP); err != nil {
panic(err)
}
}

time.Sleep(2e9)
go rd(udpl)
time.Sleep(2e9)

if mcast {
fmt.Println("LeaveGroup")
if e := udpl.LeaveGroup(mca.IP); e != nil {
panic(e)
}
}

fmt.Println("Close")
if e := udpl.Close(); e != nil {
panic(e)
}
fmt.Println("Closed")

wg.Wait()
}

func rd(udpl *net.UDPConn){
wg.Add(1)
fmt.Println("Reading")
b := make([]byte, 1024)
_, _, err := udpl.ReadFrom(b)
if err != nil {
panic(err)
}
fmt.Println("Read")
wg.Done()
}

The workaround for now seems to be to set a read timeout on the
socket, but this isn't ideal.

Regards

Albert

ron minnich

unread,
Jul 29, 2011, 11:34:18 AM7/29/11
to Albert Strasheim, golang-nuts
On Fri, Jul 29, 2011 at 8:31 AM, Albert Strasheim <ful...@gmail.com> wrote:
> Hello all
>
> We have the following code that creates a UDP socket in the main
> goroutine, reads it in another goroutine and then closes it in the
> main goroutine. We find that the close does not wake up the reading
> goroutine.

that's entirely consistent with UDP.

ron

Albert Strasheim

unread,
Jul 29, 2011, 11:46:43 AM7/29/11
to ron minnich, golang-nuts
Hello

I can appreciate your point, but is it consistent with the rest of the
net package?

Regards

Albert

Kyle Lemons

unread,
Jul 29, 2011, 1:30:24 PM7/29/11
to Albert Strasheim, ron minnich, golang-nuts
Yeah, I believe that's consistent with the tcp parts net package, and the underlying behavior on linux. There have been some discussion about changing it, but I don't think the decision was made to do so. If I find I actually need to terminate a listening/reading socket, I make an ephemeral connection so that the last accept/read fires and use some signal to indicate that it should stop.
~K

Albert Strasheim

unread,
Jul 29, 2011, 1:59:39 PM7/29/11
to Kyle Lemons, ron minnich, golang-nuts
Hello

On Fri, Jul 29, 2011 at 7:30 PM, Kyle Lemons <kev...@google.com> wrote:
> Yeah, I believe that's consistent with the tcp parts net package, and the
> underlying behavior on linux. There have been some discussion about changing
> it, but I don't think the decision was made to do so. If I find I actually
> need to terminate a listening/reading socket, I make an ephemeral connection
> so that the last accept/read fires and use some signal to indicate that it
> should stop.
> ~K

As far as I can tell from the following test code, closing a TCP
socket causes read to return EOF:

func TestTCP(t* testing.T) {
l, err := net.Listen("tcp", "localhost:12345")


if err != nil {
panic(err)
}

go func() {
c, err := l.Accept()


if err != nil {
panic(err)
}

println("server reading")
c.Read(make([]byte, 1024))
println("server read")
}()
c, err := net.Dial("tcp", "localhost:12345")


if err != nil {
panic(err)
}

go func() {
println("client reading")
_, err := c.Read(make([]byte, 1024))
fmt.Printf("err=%v\n", err)
println("client read")
}()
time.Sleep(3e9)
if err := c.Close(); err != nil {
panic(err)
}
select {}
}

Making an ephemeral connection to shut down the server seems a bit
ugly. I can't think of another language/library where I've had to
resort to that.

Regards

Albert

Kyle Lemons

unread,
Jul 29, 2011, 2:02:20 PM7/29/11
to Albert Strasheim, ron minnich, golang-nuts
a UDP read(from) is more analogous to the TCP accept than tcp read from my understanding, and they have the same behavior.  file descriptor reads (like tcp read) are different, and have the behavior you expect.

~K

Russ Cox

unread,
Jul 29, 2011, 2:02:56 PM7/29/11
to Albert Strasheim, golang-nuts
Please file a bug.
If you do r.Close() it should wake up any pending r.Read.

Russ

Reply all
Reply to author
Forward
0 new messages