From 7b51cba64a46c69a4f38edc6e24956cca9fa0b60 Mon Sep 17 00:00:00 2001 From: Ryan Leavengood Date: Tue, 1 Dec 2015 17:15:42 -0500 Subject: [PATCH] Set UDP conn to non-blocking on Linux to fix Shutdown() The call to conn.File() causes Go to call dup() and then set the resulting FD to be blocking. This sets the FD back to non-blocking, allowing Shutdown() to work properly. Fixes #279. --- server_test.go | 20 +++++++++++++++++--- udp_linux.go | 10 ++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/server_test.go b/server_test.go index 15007389..c7ae3737 100644 --- a/server_test.go +++ b/server_test.go @@ -39,9 +39,15 @@ func AnotherHelloServer(w ResponseWriter, req *Msg) { } func RunLocalUDPServer(laddr string) (*Server, string, error) { + server, l, _, err := RunLocalUDPServerWithFinChan(laddr) + + return server, l, err +} + +func RunLocalUDPServerWithFinChan(laddr string) (*Server, string, chan struct{}, error) { pc, err := net.ListenPacket("udp", laddr) if err != nil { - return nil, "", err + return nil, "", nil, err } server := &Server{PacketConn: pc, ReadTimeout: time.Hour, WriteTimeout: time.Hour} @@ -49,13 +55,16 @@ func RunLocalUDPServer(laddr string) (*Server, string, error) { waitLock.Lock() server.NotifyStartedFunc = waitLock.Unlock + fin := make(chan struct{}, 0) + go func() { server.ActivateAndServe() + close(fin) pc.Close() }() waitLock.Lock() - return server, pc.LocalAddr().String(), nil + return server, pc.LocalAddr().String(), fin, nil } func RunLocalUDPServerUnsafe(laddr string) (*Server, string, error) { @@ -448,7 +457,7 @@ func TestHandlerCloseTCP(t *testing.T) { } func TestShutdownUDP(t *testing.T) { - s, _, err := RunLocalUDPServer("127.0.0.1:0") + s, _, fin, err := RunLocalUDPServerWithFinChan("127.0.0.1:0") if err != nil { t.Fatalf("unable to run test server: %v", err) } @@ -456,6 +465,11 @@ func TestShutdownUDP(t *testing.T) { if err != nil { t.Errorf("could not shutdown test UDP server, %v", err) } + select { + case <-fin: + case <-time.After(2 * time.Second): + t.Error("Could not shutdown test UDP server. Gave up waiting") + } } type ExampleFrameLengthWriter struct { diff --git a/udp_linux.go b/udp_linux.go index 7a107857..c62d2188 100644 --- a/udp_linux.go +++ b/udp_linux.go @@ -24,6 +24,12 @@ func setUDPSocketOptions4(conn *net.UDPConn) error { if err := syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IP, syscall.IP_PKTINFO, 1); err != nil { return err } + // Calling File() above results in the connection becoming blocking, we must fix that. + // See https://github.com/miekg/dns/issues/279 + err = syscall.SetNonblock(int(file.Fd()), true) + if err != nil { + return err + } return nil } @@ -36,6 +42,10 @@ func setUDPSocketOptions6(conn *net.UDPConn) error { if err := syscall.SetsockoptInt(int(file.Fd()), syscall.IPPROTO_IPV6, syscall.IPV6_RECVPKTINFO, 1); err != nil { return err } + err = syscall.SetNonblock(int(file.Fd()), true) + if err != nil { + return err + } return nil }