Commit f7e06035 authored by Juan Batiz-Benet's avatar Juan Batiz-Benet

Merge pull request #629 from jbenet/fd-leak-fix

fixing fd leaks
parents 343940da bf1690f4
......@@ -160,7 +160,7 @@
},
{
"ImportPath": "github.com/jbenet/go-reuseport",
"Rev": "6924153aded2d61c89a83c8f0738ed4e8df9191f"
"Rev": "096958438ae3683c9f3c8ae0f7139b5ce600e6a8"
},
{
"ImportPath": "github.com/jbenet/go-sockaddr/net",
......
......@@ -18,15 +18,3 @@ func ResolveAddr(network, address string) (net.Addr, error) {
return net.ResolveUnixAddr(network, address)
}
}
// conn is a struct that stores a raddr to get around:
// * https://github.com/golang/go/issues/9661#issuecomment-71043147
// * https://gist.github.com/jbenet/5c191d698fe9ec58c49d
type conn struct {
net.Conn
raddr net.Addr
}
func (c *conn) RemoteAddr() net.Addr {
return c.raddr
}
......@@ -163,6 +163,7 @@ func dial(dialer net.Dialer, netw, addr string) (c net.Conn, err error) {
if err = file.Close(); err != nil {
syscall.Close(fd)
c.Close()
return nil, err
}
......@@ -170,19 +171,6 @@ func dial(dialer net.Dialer, netw, addr string) (c net.Conn, err error) {
return c, err
}
// there's a rare case where dial returns successfully but for some reason the
// RemoteAddr is not yet set. So, since we know what raddr should be, we just
// wrap it. This is not ideal in that sometimes getpeername() may return a
// different addr. But until this is fixed, best way to do it.
// * https://gist.github.com/jbenet/5c191d698fe9ec58c49d
// * https://github.com/golang/go/issues/9661#issuecomment-71043147
func wrapConnWithRemoteAddr(c net.Conn, raddr net.Addr) net.Conn {
if c.RemoteAddr() == nil {
return &conn{Conn: c, raddr: raddr}
}
return c // it's fine, no need to wrap.
}
func listen(netw, addr string) (fd int, err error) {
var (
family int
......@@ -256,6 +244,7 @@ func listenStream(netw, addr string) (l net.Listener, err error) {
if err = file.Close(); err != nil {
syscall.Close(fd)
l.Close()
return nil, err
}
......@@ -280,6 +269,7 @@ func listenPacket(netw, addr string) (p net.PacketConn, err error) {
if err = file.Close(); err != nil {
syscall.Close(fd)
p.Close()
return nil, err
}
......@@ -327,6 +317,7 @@ func connect(fd int, ra syscall.Sockaddr, deadline time.Time) error {
if err != nil {
return err
}
defer poller.Close()
for {
if err = poller.WaitWrite(deadline); err != nil {
......
......@@ -7,6 +7,7 @@ import (
"io"
"net"
"os"
"os/exec"
"strings"
"sync"
"testing"
......@@ -19,6 +20,7 @@ func echo(c net.Conn) {
}
func packetEcho(c net.PacketConn) {
defer c.Close()
buf := make([]byte, 65536)
for {
n, addr, err := c.ReadFrom(buf)
......@@ -501,8 +503,8 @@ func TestDialRespectsTimeout(t *testing.T) {
go func() {
c, err := d.Dial(network, raddr)
if err == nil {
errs <- errors.New("should've not connected")
c.Close()
errs <- errors.New("should've not connected")
return
}
close(errs) // success!
......@@ -534,14 +536,37 @@ func TestUnixNotSupported(t *testing.T) {
addr := tcase[1]
t.Log("testing", network, addr)
_, err := Listen(network, addr)
l, err := Listen(network, addr)
if err == nil {
l.Close()
t.Fatal("unix supported")
continue
}
}
}
func TestOpenFDs(t *testing.T) {
// this is a totally ad-hoc limit. test harnesses may add fds.
// but if this is really much higher than 20, there's obviously leaks.
limit := 20
start := time.Now()
for countOpenFiles(t) > limit {
<-time.After(time.Second)
t.Log("open fds:", countOpenFiles(t))
if time.Now().Sub(start) > (time.Second * 15) {
t.Error("fd leak!")
}
}
}
func countOpenFiles(t *testing.T) int {
out, err := exec.Command("/bin/sh", "-c", fmt.Sprintf("lsof -p %v", os.Getpid())).Output()
if err != nil {
t.Fatal(err)
}
return bytes.Count(out, []byte("\n"))
}
func getPort(a net.Addr) string {
if a == nil {
return ""
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment