Commit 4540e162 authored by Mikio Hara's avatar Mikio Hara

net: fix inconsistent error values on Accept

This change fixes inconsistent error values on Accept{,TCP,Unix}.

Updates #4856.

Change-Id: Ie3bb534c19a724cacb3ea3f3656e46c810b2123f
Reviewed-on: https://go-review.googlesource.com/8996Reviewed-by: default avatarIan Lance Taylor <iant@golang.org>
parent 310db63c
...@@ -11,6 +11,7 @@ import ( ...@@ -11,6 +11,7 @@ import (
"os" "os"
"runtime" "runtime"
"testing" "testing"
"time"
) )
func isTimeoutError(err error) bool { func isTimeoutError(err error) bool {
...@@ -424,3 +425,76 @@ func TestCloseError(t *testing.T) { ...@@ -424,3 +425,76 @@ func TestCloseError(t *testing.T) {
} }
} }
} }
// parseAcceptError parses nestedErr and reports whether it is a valid
// error value from Accept functions.
// It returns nil when nestedErr is valid.
func parseAcceptError(nestedErr error) error {
if nestedErr == nil {
return nil
}
switch err := nestedErr.(type) {
case *OpError:
if err := err.isValid(); err != nil {
return err
}
nestedErr = err.Err
goto second
}
return fmt.Errorf("unexpected type on 1st nested level: %T", nestedErr)
second:
if isPlatformError(nestedErr) {
return nil
}
switch err := nestedErr.(type) {
case *os.SyscallError:
nestedErr = err.Err
goto third
}
switch nestedErr {
case errClosing, errTimeout:
return nil
}
return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
third:
if isPlatformError(nestedErr) {
return nil
}
return fmt.Errorf("unexpected type on 3rd nested level: %T", nestedErr)
}
func TestAcceptError(t *testing.T) {
handler := func(ls *localServer, ln Listener) {
for {
ln.(*TCPListener).SetDeadline(time.Now().Add(5 * time.Millisecond))
c, err := ln.Accept()
if perr := parseAcceptError(err); perr != nil {
t.Error(perr)
}
if err != nil {
if c != nil {
t.Errorf("Accept returned non-nil interface %T(%v) with err != nil", c, c)
}
if !isTimeoutError(err) && !isTemporaryError(err) {
return
}
continue
}
c.Close()
}
}
ls, err := newLocalServer("tcp")
if err != nil {
t.Fatal(err)
}
if err := ls.buildup(handler); err != nil {
ls.teardown()
t.Fatal(err)
}
time.Sleep(100 * time.Millisecond)
ls.teardown()
}
...@@ -377,7 +377,7 @@ func (fd *netFD) accept() (netfd *netFD, err error) { ...@@ -377,7 +377,7 @@ func (fd *netFD) accept() (netfd *netFD, err error) {
var s int var s int
var rsa syscall.Sockaddr var rsa syscall.Sockaddr
if err = fd.pd.PrepareRead(); err != nil { if err = fd.pd.PrepareRead(); err != nil {
return nil, &OpError{"accept", fd.net, fd.laddr, err} return nil, err
} }
for { for {
s, rsa, err = accept(fd.sysfd) s, rsa, err = accept(fd.sysfd)
...@@ -391,7 +391,7 @@ func (fd *netFD) accept() (netfd *netFD, err error) { ...@@ -391,7 +391,7 @@ func (fd *netFD) accept() (netfd *netFD, err error) {
// before we Accept()ed it; it's a silly error, so try again. // before we Accept()ed it; it's a silly error, so try again.
continue continue
} }
return nil, &OpError{"accept", fd.net, fd.laddr, err} return nil, err
} }
break break
} }
......
...@@ -522,14 +522,14 @@ func (fd *netFD) acceptOne(rawsa []syscall.RawSockaddrAny, o *operation) (*netFD ...@@ -522,14 +522,14 @@ func (fd *netFD) acceptOne(rawsa []syscall.RawSockaddrAny, o *operation) (*netFD
// Get new socket. // Get new socket.
s, err := sysSocket(fd.family, fd.sotype, 0) s, err := sysSocket(fd.family, fd.sotype, 0)
if err != nil { if err != nil {
return nil, &OpError{"socket", fd.net, fd.laddr, err} return nil, err
} }
// Associate our new socket with IOCP. // Associate our new socket with IOCP.
netfd, err := newFD(s, fd.family, fd.sotype, fd.net) netfd, err := newFD(s, fd.family, fd.sotype, fd.net)
if err != nil { if err != nil {
closeFunc(s) closeFunc(s)
return nil, &OpError{"accept", fd.net, fd.laddr, err} return nil, err
} }
if err := netfd.init(); err != nil { if err := netfd.init(); err != nil {
fd.Close() fd.Close()
...@@ -551,7 +551,7 @@ func (fd *netFD) acceptOne(rawsa []syscall.RawSockaddrAny, o *operation) (*netFD ...@@ -551,7 +551,7 @@ func (fd *netFD) acceptOne(rawsa []syscall.RawSockaddrAny, o *operation) (*netFD
err = syscall.Setsockopt(s, syscall.SOL_SOCKET, syscall.SO_UPDATE_ACCEPT_CONTEXT, (*byte)(unsafe.Pointer(&fd.sysfd)), int32(unsafe.Sizeof(fd.sysfd))) err = syscall.Setsockopt(s, syscall.SOL_SOCKET, syscall.SO_UPDATE_ACCEPT_CONTEXT, (*byte)(unsafe.Pointer(&fd.sysfd)), int32(unsafe.Sizeof(fd.sysfd)))
if err != nil { if err != nil {
netfd.Close() netfd.Close()
return nil, &OpError{"Setsockopt", fd.net, fd.laddr, err} return nil, err
} }
return netfd, nil return netfd, nil
...@@ -577,11 +577,7 @@ func (fd *netFD) accept() (*netFD, error) { ...@@ -577,11 +577,7 @@ func (fd *netFD) accept() (*netFD, error) {
// before AcceptEx could complete. These errors relate to new // before AcceptEx could complete. These errors relate to new
// connection, not to AcceptEx, so ignore broken connection and // connection, not to AcceptEx, so ignore broken connection and
// try AcceptEx again for more connections. // try AcceptEx again for more connections.
operr, ok := err.(*OpError) errno, ok := err.(syscall.Errno)
if !ok {
return nil, err
}
errno, ok := operr.Err.(syscall.Errno)
if !ok { if !ok {
return nil, err return nil, err
} }
......
...@@ -241,7 +241,7 @@ func (l *TCPListener) AcceptTCP() (*TCPConn, error) { ...@@ -241,7 +241,7 @@ func (l *TCPListener) AcceptTCP() (*TCPConn, error) {
} }
fd, err := l.fd.accept() fd, err := l.fd.accept()
if err != nil { if err != nil {
return nil, err return nil, &OpError{Op: "accept", Net: l.fd.net, Addr: l.fd.laddr, Err: err}
} }
return newTCPConn(fd), nil return newTCPConn(fd), nil
} }
......
...@@ -81,16 +81,28 @@ func TestAcceptTimeout(t *testing.T) { ...@@ -81,16 +81,28 @@ func TestAcceptTimeout(t *testing.T) {
if _, err := ln.Accept(); !isTimeoutError(err) { if _, err := ln.Accept(); !isTimeoutError(err) {
t.Fatalf("Accept: expected err %v, got %v", errTimeout, err) t.Fatalf("Accept: expected err %v, got %v", errTimeout, err)
} }
if perr := parseAcceptError(err); perr != nil {
t.Error(perr)
}
if _, err := ln.Accept(); !isTimeoutError(err) { if _, err := ln.Accept(); !isTimeoutError(err) {
t.Fatalf("Accept: expected err %v, got %v", errTimeout, err) t.Fatalf("Accept: expected err %v, got %v", errTimeout, err)
} }
if perr := parseAcceptError(err); perr != nil {
t.Error(perr)
}
ln.(*TCPListener).SetDeadline(time.Now().Add(100 * time.Millisecond)) ln.(*TCPListener).SetDeadline(time.Now().Add(100 * time.Millisecond))
if _, err := ln.Accept(); !isTimeoutError(err) { if _, err := ln.Accept(); !isTimeoutError(err) {
t.Fatalf("Accept: expected err %v, got %v", errTimeout, err) t.Fatalf("Accept: expected err %v, got %v", errTimeout, err)
} }
if perr := parseAcceptError(err); perr != nil {
t.Error(perr)
}
if _, err := ln.Accept(); !isTimeoutError(err) { if _, err := ln.Accept(); !isTimeoutError(err) {
t.Fatalf("Accept: expected err %v, got %v", errTimeout, err) t.Fatalf("Accept: expected err %v, got %v", errTimeout, err)
} }
if perr := parseAcceptError(err); perr != nil {
t.Error(perr)
}
ln.(*TCPListener).SetDeadline(noDeadline) ln.(*TCPListener).SetDeadline(noDeadline)
errc := make(chan error) errc := make(chan error)
go func() { go func() {
...@@ -104,15 +116,9 @@ func TestAcceptTimeout(t *testing.T) { ...@@ -104,15 +116,9 @@ func TestAcceptTimeout(t *testing.T) {
default: default:
} }
ln.Close() ln.Close()
switch nerr := <-errc; err := nerr.(type) { err = <-errc
case *OpError: if perr := parseAcceptError(err); perr != nil {
if err.Err != errClosing { t.Error(perr)
t.Fatalf("Accept: expected err %v, got %v", errClosing, err)
}
default:
if err != errClosing {
t.Fatalf("Accept: expected err %v, got %v", errClosing, err)
}
} }
} }
...@@ -356,18 +362,18 @@ func TestDeadlineReset(t *testing.T) { ...@@ -356,18 +362,18 @@ func TestDeadlineReset(t *testing.T) {
} }
} }
func TestTimeoutAccept(t *testing.T) { func TestConcurrentAcceptTimeout(t *testing.T) {
switch runtime.GOOS { switch runtime.GOOS {
case "plan9": case "plan9":
t.Skipf("skipping test on %q", runtime.GOOS) t.Skipf("skipping test on %q", runtime.GOOS)
} }
ln, err := Listen("tcp", "127.0.0.1:0")
ln, err := newLocalListener("tcp")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer ln.Close() defer ln.Close()
tl := ln.(*TCPListener) ln.(*TCPListener).SetDeadline(time.Now().Add(100 * time.Millisecond))
tl.SetDeadline(time.Now().Add(100 * time.Millisecond))
errc := make(chan error, 1) errc := make(chan error, 1)
go func() { go func() {
_, err := ln.Accept() _, err := ln.Accept()
...@@ -376,9 +382,11 @@ func TestTimeoutAccept(t *testing.T) { ...@@ -376,9 +382,11 @@ func TestTimeoutAccept(t *testing.T) {
select { select {
case <-time.After(1 * time.Second): case <-time.After(1 * time.Second):
// Accept shouldn't block indefinitely // Accept shouldn't block indefinitely
t.Errorf("Accept didn't return in an expected time") t.Error("Accept didn't return in an expected time")
case <-errc: case err := <-errc:
// Pass. if perr := parseAcceptError(err); perr != nil {
t.Error(perr)
}
} }
} }
......
...@@ -99,13 +99,13 @@ func ListenUnix(net string, laddr *UnixAddr) (*UnixListener, error) { ...@@ -99,13 +99,13 @@ func ListenUnix(net string, laddr *UnixAddr) (*UnixListener, error) {
// AcceptUnix accepts the next incoming call and returns the new // AcceptUnix accepts the next incoming call and returns the new
// connection. // connection.
func (l *UnixListener) AcceptUnix() (*UnixConn, error) { func (l *UnixListener) AcceptUnix() (*UnixConn, error) {
return nil, syscall.EPLAN9 return nil, &OpError{Op: "accept", Net: "<nil>", Addr: nil, Err: syscall.EPLAN9}
} }
// Accept implements the Accept method in the Listener interface; it // Accept implements the Accept method in the Listener interface; it
// waits for the next call and returns a generic Conn. // waits for the next call and returns a generic Conn.
func (l *UnixListener) Accept() (Conn, error) { func (l *UnixListener) Accept() (Conn, error) {
return nil, syscall.EPLAN9 return nil, &OpError{Op: "accept", Net: "<nil>", Addr: nil, Err: syscall.EPLAN9}
} }
// Close stops listening on the Unix address. Already accepted // Close stops listening on the Unix address. Already accepted
......
...@@ -307,10 +307,9 @@ func (l *UnixListener) AcceptUnix() (*UnixConn, error) { ...@@ -307,10 +307,9 @@ func (l *UnixListener) AcceptUnix() (*UnixConn, error) {
} }
fd, err := l.fd.accept() fd, err := l.fd.accept()
if err != nil { if err != nil {
return nil, err return nil, &OpError{Op: "accept", Net: l.fd.net, Addr: l.fd.laddr, Err: err}
} }
c := newUnixConn(fd) return newUnixConn(fd), nil
return c, nil
} }
// Accept implements the Accept method in the Listener interface; it // Accept implements the Accept method in the Listener interface; it
......
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