net: close fds eagerly in DialTimeout
Integrates with the pollserver now.
Uses the old implementation on windows and plan9.
Fixes #2631
R=paul, iant, adg, bendaglish, rsc
CC=golang-dev
https://golang.org/cl/6815049
diff --git a/src/pkg/net/dial.go b/src/pkg/net/dial.go
index 752f81b..a85e3c6 100644
--- a/src/pkg/net/dial.go
+++ b/src/pkg/net/dial.go
@@ -5,6 +5,7 @@
package net
import (
+ "runtime"
"time"
)
@@ -36,7 +37,7 @@
return "", 0, UnknownNetworkError(net)
}
-func resolveNetAddr(op, net, addr string) (afnet string, a Addr, err error) {
+func resolveNetAddr(op, net, addr string, deadline time.Time) (afnet string, a Addr, err error) {
afnet, _, err = parseDialNetwork(net)
if err != nil {
return "", nil, &OpError{op, net, nil, err}
@@ -44,25 +45,25 @@
if op == "dial" && addr == "" {
return "", nil, &OpError{op, net, nil, errMissingAddress}
}
+ a, err = resolveAfnetAddr(afnet, addr, deadline)
+ return
+}
+
+func resolveAfnetAddr(afnet, addr string, deadline time.Time) (Addr, error) {
+ if addr == "" {
+ return nil, nil
+ }
switch afnet {
case "tcp", "tcp4", "tcp6":
- if addr != "" {
- a, err = ResolveTCPAddr(afnet, addr)
- }
+ return resolveTCPAddr(afnet, addr, deadline)
case "udp", "udp4", "udp6":
- if addr != "" {
- a, err = ResolveUDPAddr(afnet, addr)
- }
+ return resolveUDPAddr(afnet, addr, deadline)
case "ip", "ip4", "ip6":
- if addr != "" {
- a, err = ResolveIPAddr(afnet, addr)
- }
+ return resolveIPAddr(afnet, addr, deadline)
case "unix", "unixgram", "unixpacket":
- if addr != "" {
- a, err = ResolveUnixAddr(afnet, addr)
- }
+ return ResolveUnixAddr(afnet, addr)
}
- return
+ return nil, nil
}
// Dial connects to the address addr on the network net.
@@ -89,23 +90,23 @@
// Dial("ip6:ospf", "::1")
//
func Dial(net, addr string) (Conn, error) {
- _, addri, err := resolveNetAddr("dial", net, addr)
+ _, addri, err := resolveNetAddr("dial", net, addr, noDeadline)
if err != nil {
return nil, err
}
- return dialAddr(net, addr, addri)
+ return dialAddr(net, addr, addri, noDeadline)
}
-func dialAddr(net, addr string, addri Addr) (c Conn, err error) {
+func dialAddr(net, addr string, addri Addr, deadline time.Time) (c Conn, err error) {
switch ra := addri.(type) {
case *TCPAddr:
- c, err = DialTCP(net, nil, ra)
+ c, err = dialTCP(net, nil, ra, deadline)
case *UDPAddr:
- c, err = DialUDP(net, nil, ra)
+ c, err = dialUDP(net, nil, ra, deadline)
case *IPAddr:
- c, err = DialIP(net, nil, ra)
+ c, err = dialIP(net, nil, ra, deadline)
case *UnixAddr:
- c, err = DialUnix(net, nil, ra)
+ c, err = dialUnix(net, nil, ra, deadline)
default:
err = &OpError{"dial", net + " " + addr, nil, UnknownNetworkError(net)}
}
@@ -115,13 +116,31 @@
return
}
+const useDialTimeoutRace = runtime.GOOS == "windows" || runtime.GOOS == "plan9"
+
// DialTimeout acts like Dial but takes a timeout.
// The timeout includes name resolution, if required.
func DialTimeout(net, addr string, timeout time.Duration) (Conn, error) {
- // TODO(bradfitz): the timeout should be pushed down into the
- // net package's event loop, so on timeout to dead hosts we
- // don't have a goroutine sticking around for the default of
- // ~3 minutes.
+ if useDialTimeoutRace {
+ // On windows and plan9, use the relatively inefficient
+ // goroutine-racing implementation of DialTimeout that
+ // doesn't push down deadlines to the pollster.
+ // TODO: remove this once those are implemented.
+ return dialTimeoutRace(net, addr, timeout)
+ }
+ deadline := time.Now().Add(timeout)
+ _, addri, err := resolveNetAddr("dial", net, addr, deadline)
+ if err != nil {
+ return nil, err
+ }
+ return dialAddr(net, addr, addri, deadline)
+}
+
+// dialTimeoutRace is the old implementation of DialTimeout, still used
+// on operating systems where the deadline hasn't been pushed down
+// into the pollserver.
+// TODO: fix this on Windows and plan9.
+func dialTimeoutRace(net, addr string, timeout time.Duration) (Conn, error) {
t := time.NewTimer(timeout)
defer t.Stop()
type pair struct {
@@ -131,13 +150,13 @@
ch := make(chan pair, 1)
resolvedAddr := make(chan Addr, 1)
go func() {
- _, addri, err := resolveNetAddr("dial", net, addr)
+ _, addri, err := resolveNetAddr("dial", net, addr, noDeadline)
if err != nil {
ch <- pair{nil, err}
return
}
resolvedAddr <- addri // in case we need it for OpError
- c, err := dialAddr(net, addr, addri)
+ c, err := dialAddr(net, addr, addri, noDeadline)
ch <- pair{c, err}
}()
select {
@@ -175,7 +194,7 @@
// The network string net must be a stream-oriented network:
// "tcp", "tcp4", "tcp6", "unix" or "unixpacket".
func Listen(net, laddr string) (Listener, error) {
- afnet, a, err := resolveNetAddr("listen", net, laddr)
+ afnet, a, err := resolveNetAddr("listen", net, laddr, noDeadline)
if err != nil {
return nil, err
}
@@ -200,7 +219,7 @@
// The network string net must be a packet-oriented network:
// "udp", "udp4", "udp6", "ip", "ip4", "ip6" or "unixgram".
func ListenPacket(net, addr string) (PacketConn, error) {
- afnet, a, err := resolveNetAddr("listen", net, addr)
+ afnet, a, err := resolveNetAddr("listen", net, addr, noDeadline)
if err != nil {
return nil, err
}
diff --git a/src/pkg/net/dial_test.go b/src/pkg/net/dial_test.go
index 869c3cb..34a15f2 100644
--- a/src/pkg/net/dial_test.go
+++ b/src/pkg/net/dial_test.go
@@ -7,6 +7,8 @@
import (
"flag"
"fmt"
+ "io"
+ "os"
"regexp"
"runtime"
"testing"
@@ -222,3 +224,79 @@
}
}
}
+
+func TestDialTimeoutFDLeak(t *testing.T) {
+ if runtime.GOOS != "linux" {
+ // TODO(bradfitz): test on other platforms
+ t.Logf("skipping test on %s", runtime.GOOS)
+ return
+ }
+
+ ln := newLocalListener(t)
+ defer ln.Close()
+
+ type connErr struct {
+ conn Conn
+ err error
+ }
+ dials := listenerBacklog + 100
+ maxGoodConnect := listenerBacklog + 5 // empirically 131 good ones (of 128). who knows?
+ resc := make(chan connErr)
+ for i := 0; i < dials; i++ {
+ go func() {
+ conn, err := DialTimeout("tcp", ln.Addr().String(), 500*time.Millisecond)
+ resc <- connErr{conn, err}
+ }()
+ }
+
+ var firstErr string
+ var ngood int
+ var toClose []io.Closer
+ for i := 0; i < dials; i++ {
+ ce := <-resc
+ if ce.err == nil {
+ ngood++
+ if ngood > maxGoodConnect {
+ t.Errorf("%d good connects; expected at most %d", ngood, maxGoodConnect)
+ }
+ toClose = append(toClose, ce.conn)
+ continue
+ }
+ err := ce.err
+ if firstErr == "" {
+ firstErr = err.Error()
+ } else if err.Error() != firstErr {
+ t.Fatalf("inconsistent error messages: first was %q, then later %q", firstErr, err)
+ }
+ }
+ for _, c := range toClose {
+ c.Close()
+ }
+ for i := 0; i < 100; i++ {
+ if got := numFD(); got < dials {
+ // Test passes.
+ return
+ }
+ time.Sleep(10 * time.Millisecond)
+ }
+ if got := numFD(); got >= dials {
+ t.Errorf("num fds after %d timeouts = %d; want <%d", dials, got, dials)
+ }
+}
+
+func numFD() int {
+ if runtime.GOOS == "linux" {
+ f, err := os.Open("/proc/self/fd")
+ if err != nil {
+ panic(err)
+ }
+ defer f.Close()
+ names, err := f.Readdirnames(0)
+ if err != nil {
+ panic(err)
+ }
+ return len(names)
+ }
+ // All tests using this should be skipped anyway, but:
+ panic("numFDs not implemented on " + runtime.GOOS)
+}
diff --git a/src/pkg/net/fd_unix.go b/src/pkg/net/fd_unix.go
index e1d1256f..7f82f20 100644
--- a/src/pkg/net/fd_unix.go
+++ b/src/pkg/net/fd_unix.go
@@ -162,7 +162,7 @@
// TODO(rsc): This will need to be handled more efficiently,
// probably with a heap indexed by wakeup time.
- var next_deadline int64
+ var nextDeadline int64
for key, fd := range s.pending {
var t int64
var mode int
@@ -187,12 +187,12 @@
fd.wdeadline = -1
}
s.WakeFD(fd, mode, nil)
- } else if next_deadline == 0 || t < next_deadline {
- next_deadline = t
+ } else if nextDeadline == 0 || t < nextDeadline {
+ nextDeadline = t
}
}
}
- s.deadline = next_deadline
+ s.deadline = nextDeadline
}
func (s *pollServer) Run() {
@@ -332,10 +332,14 @@
func (fd *netFD) connect(ra syscall.Sockaddr) error {
err := syscall.Connect(fd.sysfd, ra)
+ hadTimeout := fd.wdeadline > 0
if err == syscall.EINPROGRESS {
if err = fd.pollServer.WaitWrite(fd); err != nil {
return err
}
+ if hadTimeout && fd.wdeadline < 0 {
+ return errTimeout
+ }
var e int
e, err = syscall.GetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR)
if err != nil {
diff --git a/src/pkg/net/iprawsock.go b/src/pkg/net/iprawsock.go
index ae21b3c..d7bffc6 100644
--- a/src/pkg/net/iprawsock.go
+++ b/src/pkg/net/iprawsock.go
@@ -6,6 +6,10 @@
package net
+import (
+ "time"
+)
+
// IPAddr represents the address of an IP end point.
type IPAddr struct {
IP IP
@@ -26,7 +30,11 @@
// "ip", "ip4" or "ip6". A literal IPv6 host address must be
// enclosed in square brackets, as in "[::]".
func ResolveIPAddr(net, addr string) (*IPAddr, error) {
- ip, err := hostToIP(net, addr)
+ return resolveIPAddr(net, addr, noDeadline)
+}
+
+func resolveIPAddr(net, addr string, deadline time.Time) (*IPAddr, error) {
+ ip, err := hostToIP(net, addr, deadline)
if err != nil {
return nil, err
}
@@ -34,7 +42,7 @@
}
// Convert "host" into IP address.
-func hostToIP(net, host string) (ip IP, err error) {
+func hostToIP(net, host string, deadline time.Time) (ip IP, err error) {
var addr IP
// Try as an IP address.
addr = ParseIP(host)
@@ -47,7 +55,7 @@
filter = ipv6only
}
// Not an IP address. Try as a DNS name.
- addrs, err1 := LookupHost(host)
+ addrs, err1 := lookupHostDeadline(host, deadline)
if err1 != nil {
err = err1
goto Error
diff --git a/src/pkg/net/iprawsock_plan9.go b/src/pkg/net/iprawsock_plan9.go
index 6de2ee3..e77c547 100644
--- a/src/pkg/net/iprawsock_plan9.go
+++ b/src/pkg/net/iprawsock_plan9.go
@@ -130,6 +130,10 @@
// netProto, which must be "ip", "ip4", or "ip6" followed by a colon
// and a protocol number or name.
func DialIP(netProto string, laddr, raddr *IPAddr) (*IPConn, error) {
+ return dialIP(netProto, laddr, raddr, noDeadline)
+}
+
+func dialIP(netProto string, laddr, raddr *IPAddr, deadline time.Time) (*IPConn, error) {
return nil, syscall.EPLAN9
}
diff --git a/src/pkg/net/iprawsock_posix.go b/src/pkg/net/iprawsock_posix.go
index d0f0b56..4d8b534 100644
--- a/src/pkg/net/iprawsock_posix.go
+++ b/src/pkg/net/iprawsock_posix.go
@@ -10,6 +10,7 @@
import (
"syscall"
+ "time"
)
func sockaddrToIP(sa syscall.Sockaddr) Addr {
@@ -163,6 +164,10 @@
// DialIP connects to the remote address raddr on the network protocol netProto,
// which must be "ip", "ip4", or "ip6" followed by a colon and a protocol number or name.
func DialIP(netProto string, laddr, raddr *IPAddr) (*IPConn, error) {
+ return dialIP(netProto, laddr, raddr, noDeadline)
+}
+
+func dialIP(netProto string, laddr, raddr *IPAddr, deadline time.Time) (*IPConn, error) {
net, proto, err := parseDialNetwork(netProto)
if err != nil {
return nil, err
@@ -175,7 +180,7 @@
if raddr == nil {
return nil, &OpError{"dial", netProto, nil, errMissingAddress}
}
- fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_RAW, proto, "dial", sockaddrToIP)
+ fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_RAW, proto, "dial", sockaddrToIP)
if err != nil {
return nil, err
}
@@ -196,7 +201,7 @@
default:
return nil, UnknownNetworkError(net)
}
- fd, err := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_RAW, proto, "listen", sockaddrToIP)
+ fd, err := internetSocket(net, laddr.toAddr(), nil, noDeadline, syscall.SOCK_RAW, proto, "listen", sockaddrToIP)
if err != nil {
return nil, err
}
diff --git a/src/pkg/net/ipsock.go b/src/pkg/net/ipsock.go
index b9b2a9b..d1fcb48 100644
--- a/src/pkg/net/ipsock.go
+++ b/src/pkg/net/ipsock.go
@@ -6,12 +6,9 @@
package net
-var supportsIPv6, supportsIPv4map bool
+import "time"
-func init() {
- sysInit()
- supportsIPv6, supportsIPv4map = probeIPv6Stack()
-}
+var supportsIPv6, supportsIPv4map = probeIPv6Stack()
func firstFavoriteAddr(filter func(IP) IP, addrs []string) (addr IP) {
if filter == nil {
@@ -103,7 +100,7 @@
}
// Convert "host:port" into IP address and port.
-func hostPortToIP(net, hostport string) (ip IP, iport int, err error) {
+func hostPortToIP(net, hostport string, deadline time.Time) (ip IP, iport int, err error) {
host, port, err := SplitHostPort(hostport)
if err != nil {
return nil, 0, err
@@ -122,7 +119,7 @@
filter = ipv6only
}
// Not an IP address. Try as a DNS name.
- addrs, err := LookupHost(host)
+ addrs, err := lookupHostDeadline(host, deadline)
if err != nil {
return nil, 0, err
}
diff --git a/src/pkg/net/ipsock_posix.go b/src/pkg/net/ipsock_posix.go
index 1718892..87a2288 100644
--- a/src/pkg/net/ipsock_posix.go
+++ b/src/pkg/net/ipsock_posix.go
@@ -6,7 +6,10 @@
package net
-import "syscall"
+import (
+ "syscall"
+ "time"
+)
// Should we try to use the IPv4 socket interface if we're
// only dealing with IPv4 sockets? As long as the host system
@@ -125,7 +128,7 @@
sockaddr(family int) (syscall.Sockaddr, error)
}
-func internetSocket(net string, laddr, raddr sockaddr, sotype, proto int, mode string, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) {
+func internetSocket(net string, laddr, raddr sockaddr, deadline time.Time, sotype, proto int, mode string, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) {
var la, ra syscall.Sockaddr
family, ipv6only := favoriteAddrFamily(net, laddr, raddr, mode)
if laddr != nil {
@@ -138,7 +141,7 @@
goto Error
}
}
- fd, err = socket(net, family, sotype, proto, ipv6only, la, ra, toAddr)
+ fd, err = socket(net, family, sotype, proto, ipv6only, la, ra, deadline, toAddr)
if err != nil {
goto Error
}
diff --git a/src/pkg/net/lookup.go b/src/pkg/net/lookup.go
index 533b351..bec93ec 100644
--- a/src/pkg/net/lookup.go
+++ b/src/pkg/net/lookup.go
@@ -4,12 +4,53 @@
package net
+import (
+ "time"
+)
+
// LookupHost looks up the given host using the local resolver.
// It returns an array of that host's addresses.
func LookupHost(host string) (addrs []string, err error) {
return lookupHost(host)
}
+func lookupHostDeadline(host string, deadline time.Time) (addrs []string, err error) {
+ if deadline.IsZero() {
+ return lookupHost(host)
+ }
+
+ // TODO(bradfitz): consider pushing the deadline down into the
+ // name resolution functions. But that involves fixing it for
+ // the native Go resolver, cgo, Windows, etc.
+ //
+ // In the meantime, just use a goroutine. Most users affected
+ // by http://golang.org/issue/2631 are due to TCP connections
+ // to unresponsive hosts, not DNS.
+ timeout := deadline.Sub(time.Now())
+ if timeout <= 0 {
+ err = errTimeout
+ return
+ }
+ t := time.NewTimer(timeout)
+ defer t.Stop()
+ type res struct {
+ addrs []string
+ err error
+ }
+ resc := make(chan res, 1)
+ go func() {
+ a, err := lookupHost(host)
+ resc <- res{a, err}
+ }()
+ select {
+ case <-t.C:
+ err = errTimeout
+ case r := <-resc:
+ addrs, err = r.addrs, r.err
+ }
+ return
+}
+
// LookupIP looks up host using the local resolver.
// It returns an array of that host's IPv4 and IPv6 addresses.
func LookupIP(host string) (addrs []IP, err error) {
diff --git a/src/pkg/net/net.go b/src/pkg/net/net.go
index d6563e0..4f0edd4 100644
--- a/src/pkg/net/net.go
+++ b/src/pkg/net/net.go
@@ -204,6 +204,8 @@
return ok && t.Temporary()
}
+var noDeadline = time.Time{}
+
type timeout interface {
Timeout() bool
}
diff --git a/src/pkg/net/sock_posix.go b/src/pkg/net/sock_posix.go
index dc5247a..a3354ea 100644
--- a/src/pkg/net/sock_posix.go
+++ b/src/pkg/net/sock_posix.go
@@ -11,12 +11,13 @@
import (
"io"
"syscall"
+ "time"
)
var listenerBacklog = maxListenerBacklog()
// Generic socket creation.
-func socket(net string, f, t, p int, ipv6only bool, ulsa, ursa syscall.Sockaddr, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) {
+func socket(net string, f, t, p int, ipv6only bool, ulsa, ursa syscall.Sockaddr, deadline time.Time, toAddr func(syscall.Sockaddr) Addr) (fd *netFD, err error) {
// See ../syscall/exec_unix.go for description of ForkLock.
syscall.ForkLock.RLock()
s, err := syscall.Socket(f, t, p)
@@ -50,12 +51,16 @@
}
if ursa != nil {
+ if !deadline.IsZero() {
+ fd.wdeadline = deadline.UnixNano()
+ }
if err = fd.connect(ursa); err != nil {
closesocket(s)
fd.Close()
return nil, err
}
fd.isConnected = true
+ fd.wdeadline = 0
}
lsa, _ := syscall.Getsockname(s)
diff --git a/src/pkg/net/tcpsock.go b/src/pkg/net/tcpsock.go
index 47fbf29..6aba1f8 100644
--- a/src/pkg/net/tcpsock.go
+++ b/src/pkg/net/tcpsock.go
@@ -6,6 +6,8 @@
package net
+import "time"
+
// TCPAddr represents the address of a TCP end point.
type TCPAddr struct {
IP IP
@@ -28,7 +30,11 @@
// "tcp4" or "tcp6". A literal IPv6 host address must be
// enclosed in square brackets, as in "[::]:80".
func ResolveTCPAddr(net, addr string) (*TCPAddr, error) {
- ip, port, err := hostPortToIP(net, addr)
+ return resolveTCPAddr(net, addr, noDeadline)
+}
+
+func resolveTCPAddr(net, addr string, deadline time.Time) (*TCPAddr, error) {
+ ip, port, err := hostPortToIP(net, addr, deadline)
if err != nil {
return nil, err
}
diff --git a/src/pkg/net/tcpsock_plan9.go b/src/pkg/net/tcpsock_plan9.go
index 4121dd8..a77633b 100644
--- a/src/pkg/net/tcpsock_plan9.go
+++ b/src/pkg/net/tcpsock_plan9.go
@@ -6,7 +6,10 @@
package net
-import "syscall"
+import (
+ "syscall"
+ "time"
+)
// TCPConn is an implementation of the Conn interface for TCP network
// connections.
@@ -36,6 +39,13 @@
// which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is
// used as the local address for the connection.
func DialTCP(net string, laddr, raddr *TCPAddr) (c *TCPConn, err error) {
+ return dialTCP(net, laddr, raddr, noDeadline)
+}
+
+func dialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time) (c *TCPConn, err error) {
+ if !deadline.IsZero() {
+ panic("net.dialTCP: deadline not implemented on Plan 9")
+ }
switch net {
case "tcp", "tcp4", "tcp6":
default:
diff --git a/src/pkg/net/tcpsock_posix.go b/src/pkg/net/tcpsock_posix.go
index 2c34d2f..09654a4 100644
--- a/src/pkg/net/tcpsock_posix.go
+++ b/src/pkg/net/tcpsock_posix.go
@@ -143,11 +143,15 @@
// which must be "tcp", "tcp4", or "tcp6". If laddr is not nil, it is used
// as the local address for the connection.
func DialTCP(net string, laddr, raddr *TCPAddr) (*TCPConn, error) {
+ return dialTCP(net, laddr, raddr, noDeadline)
+}
+
+func dialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time) (*TCPConn, error) {
if raddr == nil {
return nil, &OpError{"dial", net, nil, errMissingAddress}
}
- fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)
+ fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)
// TCP has a rarely used mechanism called a 'simultaneous connection' in
// which Dial("tcp", addr1, addr2) run on the machine at addr1 can
@@ -177,7 +181,7 @@
if err == nil {
fd.Close()
}
- fd, err = internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)
+ fd, err = internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)
}
if err != nil {
@@ -225,7 +229,7 @@
// If laddr has a port of 0, it means to listen on some available port.
// The caller can use l.Addr() to retrieve the chosen address.
func ListenTCP(net string, laddr *TCPAddr) (*TCPListener, error) {
- fd, err := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_STREAM, 0, "listen", sockaddrToTCP)
+ fd, err := internetSocket(net, laddr.toAddr(), nil, noDeadline, syscall.SOCK_STREAM, 0, "listen", sockaddrToTCP)
if err != nil {
return nil, err
}
diff --git a/src/pkg/net/udpsock.go b/src/pkg/net/udpsock.go
index 62b27d9..bf2107b 100644
--- a/src/pkg/net/udpsock.go
+++ b/src/pkg/net/udpsock.go
@@ -6,7 +6,10 @@
package net
-import "errors"
+import (
+ "errors"
+ "time"
+)
var ErrWriteToConnected = errors.New("use of WriteTo with pre-connected UDP")
@@ -32,7 +35,11 @@
// "udp4" or "udp6". A literal IPv6 host address must be
// enclosed in square brackets, as in "[::]:80".
func ResolveUDPAddr(net, addr string) (*UDPAddr, error) {
- ip, port, err := hostPortToIP(net, addr)
+ return resolveUDPAddr(net, addr, noDeadline)
+}
+
+func resolveUDPAddr(net, addr string, deadline time.Time) (*UDPAddr, error) {
+ ip, port, err := hostPortToIP(net, addr, deadline)
if err != nil {
return nil, err
}
diff --git a/src/pkg/net/udpsock_plan9.go b/src/pkg/net/udpsock_plan9.go
index aaa7e5b..c04660b 100644
--- a/src/pkg/net/udpsock_plan9.go
+++ b/src/pkg/net/udpsock_plan9.go
@@ -10,6 +10,7 @@
"errors"
"os"
"syscall"
+ "time"
)
// UDPConn is the implementation of the Conn and PacketConn
@@ -122,6 +123,13 @@
// which must be "udp", "udp4", or "udp6". If laddr is not nil, it is
// used as the local address for the connection.
func DialUDP(net string, laddr, raddr *UDPAddr) (c *UDPConn, err error) {
+ return dialUDP(net, laddr, raddr, noDeadline)
+}
+
+func dialUDP(net string, laddr, raddr *UDPAddr, deadline time.Time) (c *UDPConn, err error) {
+ if !deadline.IsZero() {
+ panic("net.dialUDP: deadline not implemented on Plan 9")
+ }
switch net {
case "udp", "udp4", "udp6":
default:
diff --git a/src/pkg/net/udpsock_posix.go b/src/pkg/net/udpsock_posix.go
index e075380..f6e2c17 100644
--- a/src/pkg/net/udpsock_posix.go
+++ b/src/pkg/net/udpsock_posix.go
@@ -8,7 +8,10 @@
package net
-import "syscall"
+import (
+ "syscall"
+ "time"
+)
func sockaddrToUDP(sa syscall.Sockaddr) Addr {
switch sa := sa.(type) {
@@ -160,6 +163,10 @@
// which must be "udp", "udp4", or "udp6". If laddr is not nil, it is used
// as the local address for the connection.
func DialUDP(net string, laddr, raddr *UDPAddr) (*UDPConn, error) {
+ return dialUDP(net, laddr, raddr, noDeadline)
+}
+
+func dialUDP(net string, laddr, raddr *UDPAddr, deadline time.Time) (*UDPConn, error) {
switch net {
case "udp", "udp4", "udp6":
default:
@@ -168,7 +175,7 @@
if raddr == nil {
return nil, &OpError{"dial", net, nil, errMissingAddress}
}
- fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), syscall.SOCK_DGRAM, 0, "dial", sockaddrToUDP)
+ fd, err := internetSocket(net, laddr.toAddr(), raddr.toAddr(), deadline, syscall.SOCK_DGRAM, 0, "dial", sockaddrToUDP)
if err != nil {
return nil, err
}
@@ -188,7 +195,7 @@
if laddr == nil {
return nil, &OpError{"listen", net, nil, errMissingAddress}
}
- fd, err := internetSocket(net, laddr.toAddr(), nil, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP)
+ fd, err := internetSocket(net, laddr.toAddr(), nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP)
if err != nil {
return nil, err
}
@@ -208,7 +215,7 @@
if gaddr == nil || gaddr.IP == nil {
return nil, &OpError{"listenmulticast", net, nil, errMissingAddress}
}
- fd, err := internetSocket(net, gaddr.toAddr(), nil, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP)
+ fd, err := internetSocket(net, gaddr.toAddr(), nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen", sockaddrToUDP)
if err != nil {
return nil, err
}
diff --git a/src/pkg/net/unixsock_plan9.go b/src/pkg/net/unixsock_plan9.go
index 2140375..342e26f 100644
--- a/src/pkg/net/unixsock_plan9.go
+++ b/src/pkg/net/unixsock_plan9.go
@@ -139,6 +139,10 @@
// which must be "unix" or "unixgram". If laddr is not nil, it is
// used as the local address for the connection.
func DialUnix(net string, laddr, raddr *UnixAddr) (*UnixConn, error) {
+ return dialUnix(net, laddr, raddr, noDeadline)
+}
+
+func dialUnix(net string, laddr, raddr *UnixAddr, deadline time.Time) (*UnixConn, error) {
return nil, syscall.EPLAN9
}
diff --git a/src/pkg/net/unixsock_posix.go b/src/pkg/net/unixsock_posix.go
index 2bef5ea..f7cc074 100644
--- a/src/pkg/net/unixsock_posix.go
+++ b/src/pkg/net/unixsock_posix.go
@@ -14,7 +14,7 @@
"time"
)
-func unixSocket(net string, laddr, raddr *UnixAddr, mode string) (fd *netFD, err error) {
+func unixSocket(net string, laddr, raddr *UnixAddr, mode string, deadline time.Time) (fd *netFD, err error) {
var sotype int
switch net {
default:
@@ -59,7 +59,7 @@
f = sockaddrToUnixpacket
}
- fd, err = socket(net, syscall.AF_UNIX, sotype, 0, false, la, ra, f)
+ fd, err = socket(net, syscall.AF_UNIX, sotype, 0, false, la, ra, deadline, f)
if err != nil {
goto Error
}
@@ -229,7 +229,11 @@
// which must be "unix" or "unixgram". If laddr is not nil, it is used
// as the local address for the connection.
func DialUnix(net string, laddr, raddr *UnixAddr) (*UnixConn, error) {
- fd, err := unixSocket(net, laddr, raddr, "dial")
+ return dialUnix(net, laddr, raddr, noDeadline)
+}
+
+func dialUnix(net string, laddr, raddr *UnixAddr, deadline time.Time) (*UnixConn, error) {
+ fd, err := unixSocket(net, laddr, raddr, "dial", deadline)
if err != nil {
return nil, err
}
@@ -253,7 +257,7 @@
if laddr != nil {
laddr = &UnixAddr{laddr.Name, net} // make our own copy
}
- fd, err := unixSocket(net, laddr, nil, "listen")
+ fd, err := unixSocket(net, laddr, nil, "listen", noDeadline)
if err != nil {
return nil, err
}
@@ -344,7 +348,7 @@
if laddr == nil {
return nil, &OpError{"listen", net, nil, errMissingAddress}
}
- fd, err := unixSocket(net, laddr, nil, "listen")
+ fd, err := unixSocket(net, laddr, nil, "listen", noDeadline)
if err != nil {
return nil, err
}