net: introduce net.Error interface
Adds two more methods, Timeout and Temporary.
Implemented by os.Errno too. The intent is to make
the checks for os.EAGAIN a little less clunky.
It should also let us clean up a bug that Mike Solomon
pointed out: if a network server gets an "out of file descriptors"
error from Accept, the listener should not stop.
It will be able to check this because that error would
have Temporary() == true.
Also clean up some underscore names.
Fixes #442.
R=r
CC=golang-dev, msolo
https://golang.org/cl/957045
diff --git a/src/pkg/net/fd.go b/src/pkg/net/fd.go
index 02f7319..1fa537c 100644
--- a/src/pkg/net/fd.go
+++ b/src/pkg/net/fd.go
@@ -7,6 +7,7 @@
package net
import (
+ "io"
"once"
"os"
"sync"
@@ -44,6 +45,12 @@
ncr, ncw int
}
+type InvalidConnError struct{}
+
+func (e *InvalidConnError) String() string { return "invalid net.Conn" }
+func (e *InvalidConnError) Temporary() bool { return false }
+func (e *InvalidConnError) Timeout() bool { return false }
+
// A pollServer helps FDs determine when to retry a non-blocking
// read or write after they get EAGAIN. When an FD needs to wait,
// send the fd on s.cr (for a read) or s.cw (for a write) to pass the
@@ -342,13 +349,6 @@
fd.sysmu.Unlock()
}
-func isEAGAIN(e os.Error) bool {
- if e1, ok := e.(*os.PathError); ok {
- return e1.Error == os.EAGAIN
- }
- return e == os.EAGAIN
-}
-
func (fd *netFD) Close() os.Error {
if fd == nil || fd.sysfile == nil {
return os.EINVAL
@@ -374,17 +374,24 @@
} else {
fd.rdeadline = 0
}
+ var oserr os.Error
for {
- n, err = fd.sysfile.Read(p)
- if isEAGAIN(err) && fd.rdeadline >= 0 {
+ var errno int
+ n, errno = syscall.Read(fd.sysfile.Fd(), p)
+ if errno == syscall.EAGAIN && fd.rdeadline >= 0 {
pollserver.WaitRead(fd)
continue
}
+ if errno != 0 {
+ n = 0
+ oserr = os.Errno(errno)
+ } else if n == 0 && errno == 0 && fd.proto != syscall.SOCK_DGRAM {
+ err = os.EOF
+ }
break
}
- if fd.proto == syscall.SOCK_DGRAM && err == os.EOF {
- // 0 in datagram protocol just means 0-length packet
- err = nil
+ if oserr != nil {
+ err = &OpError{"read", fd.net, fd.raddr, oserr}
}
return
}
@@ -402,6 +409,7 @@
} else {
fd.rdeadline = 0
}
+ var oserr os.Error
for {
var errno int
n, sa, errno = syscall.Recvfrom(fd.sysfd, p, 0)
@@ -411,10 +419,13 @@
}
if errno != 0 {
n = 0
- err = &os.PathError{"recvfrom", fd.sysfile.Name(), os.Errno(errno)}
+ oserr = os.Errno(errno)
}
break
}
+ if oserr != nil {
+ err = &OpError{"read", fd.net, fd.laddr, oserr}
+ }
return
}
@@ -431,25 +442,32 @@
} else {
fd.wdeadline = 0
}
- err = nil
nn := 0
- first := true // force at least one Write, to send 0-length datagram packets
- for nn < len(p) || first {
- first = false
- n, err = fd.sysfile.Write(p[nn:])
+ var oserr os.Error
+ for {
+ n, errno := syscall.Write(fd.sysfile.Fd(), p[nn:])
if n > 0 {
nn += n
}
if nn == len(p) {
break
}
- if isEAGAIN(err) && fd.wdeadline >= 0 {
+ if errno == syscall.EAGAIN && fd.wdeadline >= 0 {
pollserver.WaitWrite(fd)
continue
}
- if n == 0 || err != nil {
+ if errno != 0 {
+ n = 0
+ oserr = os.Errno(errno)
break
}
+ if n == 0 {
+ oserr = io.ErrUnexpectedEOF
+ break
+ }
+ }
+ if oserr != nil {
+ err = &OpError{"write", fd.net, fd.raddr, oserr}
}
return nn, err
}
@@ -467,7 +485,7 @@
} else {
fd.wdeadline = 0
}
- err = nil
+ var oserr os.Error
for {
errno := syscall.Sendto(fd.sysfd, p, 0, sa)
if errno == syscall.EAGAIN && fd.wdeadline >= 0 {
@@ -475,12 +493,14 @@
continue
}
if errno != 0 {
- err = &os.PathError{"sendto", fd.sysfile.Name(), os.Errno(errno)}
+ oserr = os.Errno(errno)
}
break
}
- if err == nil {
+ if oserr == nil {
n = len(p)
+ } else {
+ err = &OpError{"write", fd.net, fd.raddr, oserr}
}
return
}