Commit fb4b4342 authored by Ian Lance Taylor's avatar Ian Lance Taylor

os, net, internal/poll: return consistent error for closed socket

In the past we returned "use of closed network connection" when using
a closed network descriptor in some way. In CL 36799 that was changed
to return "use of closed file or network connection". Because programs
have no access to a value of this error type (see issue #4373) they
resort to doing direct string comparisons (see issue #19252). This CL
restores the old error string so that we don't break programs
unnecessarily with the 1.9 release.

This adds a test to the net package for the expected string.

For symmetry check that the os package returns the expected error,
which for os already exists as os.ErrClosed.

Updates #4373.
Fixed #19252.

Change-Id: I5b83fd12cfa03501a077cad9336499b819f4a38b
Reviewed-on: https://go-review.googlesource.com/39997
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarBrad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: default avatarRuss Cox <rsc@golang.org>
parent 2fb2ebc3
...@@ -11,8 +11,23 @@ package poll ...@@ -11,8 +11,23 @@ package poll
import "errors" import "errors"
// ErrClosing is returned when a descriptor is used after it has been closed. // ErrNetClosing is returned when a network descriptor is used after
var ErrClosing = errors.New("use of closed file or network connection") // it has been closed. Keep this string consistent because of issue
// #4373: since historically programs have not been able to detect
// this error, they look for the string.
var ErrNetClosing = errors.New("use of closed network connection")
// ErrFileClosing is returned when a file descriptor is used after it
// has been closed.
var ErrFileClosing = errors.New("use of closed file")
// Return the appropriate closing error based on isFile.
func errClosing(isFile bool) error {
if isFile {
return ErrFileClosing
}
return ErrNetClosing
}
// ErrTimeout is returned for an expired deadline. // ErrTimeout is returned for an expired deadline.
var ErrTimeout error = &TimeoutError{} var ErrTimeout error = &TimeoutError{}
......
...@@ -198,7 +198,7 @@ func runtime_Semrelease(sema *uint32) ...@@ -198,7 +198,7 @@ func runtime_Semrelease(sema *uint32)
// It returns an error when fd cannot be used. // It returns an error when fd cannot be used.
func (fd *FD) incref() error { func (fd *FD) incref() error {
if !fd.fdmu.incref() { if !fd.fdmu.incref() {
return ErrClosing return errClosing(fd.isFile)
} }
return nil return nil
} }
...@@ -217,7 +217,7 @@ func (fd *FD) decref() error { ...@@ -217,7 +217,7 @@ func (fd *FD) decref() error {
// It returns an error when fd cannot be used for reading. // It returns an error when fd cannot be used for reading.
func (fd *FD) readLock() error { func (fd *FD) readLock() error {
if !fd.fdmu.rwlock(true) { if !fd.fdmu.rwlock(true) {
return ErrClosing return errClosing(fd.isFile)
} }
return nil return nil
} }
...@@ -235,7 +235,7 @@ func (fd *FD) readUnlock() { ...@@ -235,7 +235,7 @@ func (fd *FD) readUnlock() {
// It returns an error when fd cannot be used for writing. // It returns an error when fd cannot be used for writing.
func (fd *FD) writeLock() error { func (fd *FD) writeLock() error {
if !fd.fdmu.rwlock(false) { if !fd.fdmu.rwlock(false) {
return ErrClosing return errClosing(fd.isFile)
} }
return nil return nil
} }
......
...@@ -29,6 +29,12 @@ type FD struct { ...@@ -29,6 +29,12 @@ type FD struct {
wtimer *time.Timer wtimer *time.Timer
rtimedout atomicBool // set true when read deadline has been reached rtimedout atomicBool // set true when read deadline has been reached
wtimedout atomicBool // set true when write deadline has been reached wtimedout atomicBool // set true when write deadline has been reached
// Whether this is a normal file.
// On Plan 9 we do not use this package for ordinary files,
// so this is always false, but the field is present because
// shared code in fd_mutex.go checks it.
isFile bool
} }
// We need this to close out a file descriptor when it is unlocked, // We need this to close out a file descriptor when it is unlocked,
...@@ -45,7 +51,7 @@ func (fd *FD) destroy() error { ...@@ -45,7 +51,7 @@ func (fd *FD) destroy() error {
// is in the net package. // is in the net package.
func (fd *FD) Close() error { func (fd *FD) Close() error {
if !fd.fdmu.increfAndClose() { if !fd.fdmu.increfAndClose() {
return ErrClosing return errClosing(fd.isFile)
} }
return nil return nil
} }
......
...@@ -25,27 +25,27 @@ func (pd *pollDesc) evict() { ...@@ -25,27 +25,27 @@ func (pd *pollDesc) evict() {
} }
} }
func (pd *pollDesc) prepare(mode int) error { func (pd *pollDesc) prepare(mode int, isFile bool) error {
if pd.closing { if pd.closing {
return ErrClosing return errClosing(isFile)
} }
return nil return nil
} }
func (pd *pollDesc) prepareRead() error { return pd.prepare('r') } func (pd *pollDesc) prepareRead(isFile bool) error { return pd.prepare('r', isFile) }
func (pd *pollDesc) prepareWrite() error { return pd.prepare('w') } func (pd *pollDesc) prepareWrite(isFile bool) error { return pd.prepare('w', isFile) }
func (pd *pollDesc) wait(mode int) error { func (pd *pollDesc) wait(mode int, isFile bool) error {
if pd.closing { if pd.closing {
return ErrClosing return errClosing(isFile)
} }
return ErrTimeout return ErrTimeout
} }
func (pd *pollDesc) waitRead() error { return pd.wait('r') } func (pd *pollDesc) waitRead(isFile bool) error { return pd.wait('r', isFile) }
func (pd *pollDesc) waitWrite() error { return pd.wait('w') } func (pd *pollDesc) waitWrite(isFile bool) error { return pd.wait('w', isFile) }
func (pd *pollDesc) waitCanceled(mode int) {} func (pd *pollDesc) waitCanceled(mode int) {}
......
...@@ -62,36 +62,36 @@ func (pd *pollDesc) evict() { ...@@ -62,36 +62,36 @@ func (pd *pollDesc) evict() {
runtime_pollUnblock(pd.runtimeCtx) runtime_pollUnblock(pd.runtimeCtx)
} }
func (pd *pollDesc) prepare(mode int) error { func (pd *pollDesc) prepare(mode int, isFile bool) error {
if pd.runtimeCtx == 0 { if pd.runtimeCtx == 0 {
return nil return nil
} }
res := runtime_pollReset(pd.runtimeCtx, mode) res := runtime_pollReset(pd.runtimeCtx, mode)
return convertErr(res) return convertErr(res, isFile)
} }
func (pd *pollDesc) prepareRead() error { func (pd *pollDesc) prepareRead(isFile bool) error {
return pd.prepare('r') return pd.prepare('r', isFile)
} }
func (pd *pollDesc) prepareWrite() error { func (pd *pollDesc) prepareWrite(isFile bool) error {
return pd.prepare('w') return pd.prepare('w', isFile)
} }
func (pd *pollDesc) wait(mode int) error { func (pd *pollDesc) wait(mode int, isFile bool) error {
if pd.runtimeCtx == 0 { if pd.runtimeCtx == 0 {
return errors.New("waiting for unsupported file type") return errors.New("waiting for unsupported file type")
} }
res := runtime_pollWait(pd.runtimeCtx, mode) res := runtime_pollWait(pd.runtimeCtx, mode)
return convertErr(res) return convertErr(res, isFile)
} }
func (pd *pollDesc) waitRead() error { func (pd *pollDesc) waitRead(isFile bool) error {
return pd.wait('r') return pd.wait('r', isFile)
} }
func (pd *pollDesc) waitWrite() error { func (pd *pollDesc) waitWrite(isFile bool) error {
return pd.wait('w') return pd.wait('w', isFile)
} }
func (pd *pollDesc) waitCanceled(mode int) { func (pd *pollDesc) waitCanceled(mode int) {
...@@ -101,12 +101,12 @@ func (pd *pollDesc) waitCanceled(mode int) { ...@@ -101,12 +101,12 @@ func (pd *pollDesc) waitCanceled(mode int) {
runtime_pollWaitCanceled(pd.runtimeCtx, mode) runtime_pollWaitCanceled(pd.runtimeCtx, mode)
} }
func convertErr(res int) error { func convertErr(res int, isFile bool) error {
switch res { switch res {
case 0: case 0:
return nil return nil
case 1: case 1:
return ErrClosing return errClosing(isFile)
case 2: case 2:
return ErrTimeout return ErrTimeout
} }
......
...@@ -20,17 +20,17 @@ var eofErrorTests = []struct { ...@@ -20,17 +20,17 @@ var eofErrorTests = []struct {
}{ }{
{100, nil, &FD{ZeroReadIsEOF: true}, nil}, {100, nil, &FD{ZeroReadIsEOF: true}, nil},
{100, io.EOF, &FD{ZeroReadIsEOF: true}, io.EOF}, {100, io.EOF, &FD{ZeroReadIsEOF: true}, io.EOF},
{100, ErrClosing, &FD{ZeroReadIsEOF: true}, ErrClosing}, {100, ErrNetClosing, &FD{ZeroReadIsEOF: true}, ErrNetClosing},
{0, nil, &FD{ZeroReadIsEOF: true}, io.EOF}, {0, nil, &FD{ZeroReadIsEOF: true}, io.EOF},
{0, io.EOF, &FD{ZeroReadIsEOF: true}, io.EOF}, {0, io.EOF, &FD{ZeroReadIsEOF: true}, io.EOF},
{0, ErrClosing, &FD{ZeroReadIsEOF: true}, ErrClosing}, {0, ErrNetClosing, &FD{ZeroReadIsEOF: true}, ErrNetClosing},
{100, nil, &FD{ZeroReadIsEOF: false}, nil}, {100, nil, &FD{ZeroReadIsEOF: false}, nil},
{100, io.EOF, &FD{ZeroReadIsEOF: false}, io.EOF}, {100, io.EOF, &FD{ZeroReadIsEOF: false}, io.EOF},
{100, ErrClosing, &FD{ZeroReadIsEOF: false}, ErrClosing}, {100, ErrNetClosing, &FD{ZeroReadIsEOF: false}, ErrNetClosing},
{0, nil, &FD{ZeroReadIsEOF: false}, nil}, {0, nil, &FD{ZeroReadIsEOF: false}, nil},
{0, io.EOF, &FD{ZeroReadIsEOF: false}, io.EOF}, {0, io.EOF, &FD{ZeroReadIsEOF: false}, io.EOF},
{0, ErrClosing, &FD{ZeroReadIsEOF: false}, ErrClosing}, {0, ErrNetClosing, &FD{ZeroReadIsEOF: false}, ErrNetClosing},
} }
func TestEOFError(t *testing.T) { func TestEOFError(t *testing.T) {
......
...@@ -33,11 +33,23 @@ type FD struct { ...@@ -33,11 +33,23 @@ type FD struct {
// Whether a zero byte read indicates EOF. This is false for a // Whether a zero byte read indicates EOF. This is false for a
// message based socket connection. // message based socket connection.
ZeroReadIsEOF bool ZeroReadIsEOF bool
// Whether this is a file rather than a network socket.
isFile bool
} }
// Init initializes the FD. The Sysfd field should already be set. // Init initializes the FD. The Sysfd field should already be set.
// This can be called multiple times on a single FD. // This can be called multiple times on a single FD.
func (fd *FD) Init() error { // The net argument is a network name from the net package (e.g., "tcp"),
// or "file".
func (fd *FD) Init(net string, pollable bool) error {
// We don't actually care about the various network types.
if net == "file" {
fd.isFile = true
}
if !pollable {
return nil
}
return fd.pd.init(fd) return fd.pd.init(fd)
} }
...@@ -56,13 +68,13 @@ func (fd *FD) destroy() error { ...@@ -56,13 +68,13 @@ func (fd *FD) destroy() error {
// destroy method when there are no remaining references. // destroy method when there are no remaining references.
func (fd *FD) Close() error { func (fd *FD) Close() error {
if !fd.fdmu.increfAndClose() { if !fd.fdmu.increfAndClose() {
return ErrClosing return errClosing(fd.isFile)
} }
// Unblock any I/O. Once it all unblocks and returns, // Unblock any I/O. Once it all unblocks and returns,
// so that it cannot be referring to fd.sysfd anymore, // so that it cannot be referring to fd.sysfd anymore,
// the final decref will close fd.sysfd. This should happen // the final decref will close fd.sysfd. This should happen
// fairly quickly, since all the I/O is non-blocking, and any // fairly quickly, since all the I/O is non-blocking, and any
// attempts to block in the pollDesc will return ErrClosing. // attempts to block in the pollDesc will return errClosing(fd.isFile).
fd.pd.evict() fd.pd.evict()
// The call to decref will call destroy if there are no other // The call to decref will call destroy if there are no other
// references. // references.
...@@ -99,7 +111,7 @@ func (fd *FD) Read(p []byte) (int, error) { ...@@ -99,7 +111,7 @@ func (fd *FD) Read(p []byte) (int, error) {
// TODO(bradfitz): make it wait for readability? (Issue 15735) // TODO(bradfitz): make it wait for readability? (Issue 15735)
return 0, nil return 0, nil
} }
if err := fd.pd.prepareRead(); err != nil { if err := fd.pd.prepareRead(fd.isFile); err != nil {
return 0, err return 0, err
} }
if fd.IsStream && len(p) > maxRW { if fd.IsStream && len(p) > maxRW {
...@@ -110,7 +122,7 @@ func (fd *FD) Read(p []byte) (int, error) { ...@@ -110,7 +122,7 @@ func (fd *FD) Read(p []byte) (int, error) {
if err != nil { if err != nil {
n = 0 n = 0
if err == syscall.EAGAIN { if err == syscall.EAGAIN {
if err = fd.pd.waitRead(); err == nil { if err = fd.pd.waitRead(fd.isFile); err == nil {
continue continue
} }
} }
...@@ -146,7 +158,7 @@ func (fd *FD) ReadFrom(p []byte) (int, syscall.Sockaddr, error) { ...@@ -146,7 +158,7 @@ func (fd *FD) ReadFrom(p []byte) (int, syscall.Sockaddr, error) {
return 0, nil, err return 0, nil, err
} }
defer fd.readUnlock() defer fd.readUnlock()
if err := fd.pd.prepareRead(); err != nil { if err := fd.pd.prepareRead(fd.isFile); err != nil {
return 0, nil, err return 0, nil, err
} }
for { for {
...@@ -154,7 +166,7 @@ func (fd *FD) ReadFrom(p []byte) (int, syscall.Sockaddr, error) { ...@@ -154,7 +166,7 @@ func (fd *FD) ReadFrom(p []byte) (int, syscall.Sockaddr, error) {
if err != nil { if err != nil {
n = 0 n = 0
if err == syscall.EAGAIN { if err == syscall.EAGAIN {
if err = fd.pd.waitRead(); err == nil { if err = fd.pd.waitRead(fd.isFile); err == nil {
continue continue
} }
} }
...@@ -170,7 +182,7 @@ func (fd *FD) ReadMsg(p []byte, oob []byte) (int, int, int, syscall.Sockaddr, er ...@@ -170,7 +182,7 @@ func (fd *FD) ReadMsg(p []byte, oob []byte) (int, int, int, syscall.Sockaddr, er
return 0, 0, 0, nil, err return 0, 0, 0, nil, err
} }
defer fd.readUnlock() defer fd.readUnlock()
if err := fd.pd.prepareRead(); err != nil { if err := fd.pd.prepareRead(fd.isFile); err != nil {
return 0, 0, 0, nil, err return 0, 0, 0, nil, err
} }
for { for {
...@@ -178,7 +190,7 @@ func (fd *FD) ReadMsg(p []byte, oob []byte) (int, int, int, syscall.Sockaddr, er ...@@ -178,7 +190,7 @@ func (fd *FD) ReadMsg(p []byte, oob []byte) (int, int, int, syscall.Sockaddr, er
if err != nil { if err != nil {
// TODO(dfc) should n and oobn be set to 0 // TODO(dfc) should n and oobn be set to 0
if err == syscall.EAGAIN { if err == syscall.EAGAIN {
if err = fd.pd.waitRead(); err == nil { if err = fd.pd.waitRead(fd.isFile); err == nil {
continue continue
} }
} }
...@@ -194,7 +206,7 @@ func (fd *FD) Write(p []byte) (int, error) { ...@@ -194,7 +206,7 @@ func (fd *FD) Write(p []byte) (int, error) {
return 0, err return 0, err
} }
defer fd.writeUnlock() defer fd.writeUnlock()
if err := fd.pd.prepareWrite(); err != nil { if err := fd.pd.prepareWrite(fd.isFile); err != nil {
return 0, err return 0, err
} }
var nn int var nn int
...@@ -211,7 +223,7 @@ func (fd *FD) Write(p []byte) (int, error) { ...@@ -211,7 +223,7 @@ func (fd *FD) Write(p []byte) (int, error) {
return nn, err return nn, err
} }
if err == syscall.EAGAIN { if err == syscall.EAGAIN {
if err = fd.pd.waitWrite(); err == nil { if err = fd.pd.waitWrite(fd.isFile); err == nil {
continue continue
} }
} }
...@@ -261,13 +273,13 @@ func (fd *FD) WriteTo(p []byte, sa syscall.Sockaddr) (int, error) { ...@@ -261,13 +273,13 @@ func (fd *FD) WriteTo(p []byte, sa syscall.Sockaddr) (int, error) {
return 0, err return 0, err
} }
defer fd.writeUnlock() defer fd.writeUnlock()
if err := fd.pd.prepareWrite(); err != nil { if err := fd.pd.prepareWrite(fd.isFile); err != nil {
return 0, err return 0, err
} }
for { for {
err := syscall.Sendto(fd.Sysfd, p, 0, sa) err := syscall.Sendto(fd.Sysfd, p, 0, sa)
if err == syscall.EAGAIN { if err == syscall.EAGAIN {
if err = fd.pd.waitWrite(); err == nil { if err = fd.pd.waitWrite(fd.isFile); err == nil {
continue continue
} }
} }
...@@ -284,13 +296,13 @@ func (fd *FD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (int, int, err ...@@ -284,13 +296,13 @@ func (fd *FD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (int, int, err
return 0, 0, err return 0, 0, err
} }
defer fd.writeUnlock() defer fd.writeUnlock()
if err := fd.pd.prepareWrite(); err != nil { if err := fd.pd.prepareWrite(fd.isFile); err != nil {
return 0, 0, err return 0, 0, err
} }
for { for {
n, err := syscall.SendmsgN(fd.Sysfd, p, oob, sa, 0) n, err := syscall.SendmsgN(fd.Sysfd, p, oob, sa, 0)
if err == syscall.EAGAIN { if err == syscall.EAGAIN {
if err = fd.pd.waitWrite(); err == nil { if err = fd.pd.waitWrite(fd.isFile); err == nil {
continue continue
} }
} }
...@@ -308,7 +320,7 @@ func (fd *FD) Accept() (int, syscall.Sockaddr, string, error) { ...@@ -308,7 +320,7 @@ func (fd *FD) Accept() (int, syscall.Sockaddr, string, error) {
} }
defer fd.readUnlock() defer fd.readUnlock()
if err := fd.pd.prepareRead(); err != nil { if err := fd.pd.prepareRead(fd.isFile); err != nil {
return -1, nil, "", err return -1, nil, "", err
} }
for { for {
...@@ -318,7 +330,7 @@ func (fd *FD) Accept() (int, syscall.Sockaddr, string, error) { ...@@ -318,7 +330,7 @@ func (fd *FD) Accept() (int, syscall.Sockaddr, string, error) {
} }
switch err { switch err {
case syscall.EAGAIN: case syscall.EAGAIN:
if err = fd.pd.waitRead(); err == nil { if err = fd.pd.waitRead(fd.isFile); err == nil {
continue continue
} }
case syscall.ECONNABORTED: case syscall.ECONNABORTED:
...@@ -353,7 +365,7 @@ func (fd *FD) ReadDirent(buf []byte) (int, error) { ...@@ -353,7 +365,7 @@ func (fd *FD) ReadDirent(buf []byte) (int, error) {
if err != nil { if err != nil {
n = 0 n = 0
if err == syscall.EAGAIN { if err == syscall.EAGAIN {
if err = fd.pd.waitRead(); err == nil { if err = fd.pd.waitRead(fd.isFile); err == nil {
continue continue
} }
} }
...@@ -385,5 +397,5 @@ func (fd *FD) Fstat(s *syscall.Stat_t) error { ...@@ -385,5 +397,5 @@ func (fd *FD) Fstat(s *syscall.Stat_t) error {
// WaitWrite waits until data can be read from fd. // WaitWrite waits until data can be read from fd.
func (fd *FD) WaitWrite() error { func (fd *FD) WaitWrite() error {
return fd.pd.waitWrite() return fd.pd.waitWrite(fd.isFile)
} }
...@@ -160,7 +160,7 @@ func (s *ioSrv) ExecIO(o *operation, name string, submit func(o *operation) erro ...@@ -160,7 +160,7 @@ func (s *ioSrv) ExecIO(o *operation, name string, submit func(o *operation) erro
fd := o.fd fd := o.fd
// Notify runtime netpoll about starting IO. // Notify runtime netpoll about starting IO.
err := fd.pd.prepare(int(o.mode)) err := fd.pd.prepare(int(o.mode), fd.isFile)
if err != nil { if err != nil {
return 0, err return 0, err
} }
...@@ -188,7 +188,7 @@ func (s *ioSrv) ExecIO(o *operation, name string, submit func(o *operation) erro ...@@ -188,7 +188,7 @@ func (s *ioSrv) ExecIO(o *operation, name string, submit func(o *operation) erro
return 0, err return 0, err
} }
// Wait for our request to complete. // Wait for our request to complete.
err = fd.pd.wait(int(o.mode)) err = fd.pd.wait(int(o.mode), fd.isFile)
if err == nil { if err == nil {
// All is good. Extract our IO results and return. // All is good. Extract our IO results and return.
if o.errno != 0 { if o.errno != 0 {
...@@ -200,7 +200,7 @@ func (s *ioSrv) ExecIO(o *operation, name string, submit func(o *operation) erro ...@@ -200,7 +200,7 @@ func (s *ioSrv) ExecIO(o *operation, name string, submit func(o *operation) erro
// IO is interrupted by "close" or "timeout" // IO is interrupted by "close" or "timeout"
netpollErr := err netpollErr := err
switch netpollErr { switch netpollErr {
case ErrClosing, ErrTimeout: case ErrNetClosing, ErrFileClosing, ErrTimeout:
// will deal with those. // will deal with those.
default: default:
panic("unexpected runtime.netpoll error: " + netpollErr.Error()) panic("unexpected runtime.netpoll error: " + netpollErr.Error())
...@@ -380,7 +380,7 @@ func (fd *FD) destroy() error { ...@@ -380,7 +380,7 @@ func (fd *FD) destroy() error {
// the destroy method when there are no remaining references. // the destroy method when there are no remaining references.
func (fd *FD) Close() error { func (fd *FD) Close() error {
if !fd.fdmu.increfAndClose() { if !fd.fdmu.increfAndClose() {
return ErrClosing return errClosing(fd.isFile)
} }
// unblock pending reader and writer // unblock pending reader and writer
fd.pd.evict() fd.pd.evict()
......
...@@ -37,7 +37,7 @@ func SendFile(dstFD *FD, src int, pos, remain int64) (int64, error) { ...@@ -37,7 +37,7 @@ func SendFile(dstFD *FD, src int, pos, remain int64) (int64, error) {
break break
} }
if err1 == syscall.EAGAIN { if err1 == syscall.EAGAIN {
if err1 = dstFD.pd.waitWrite(); err1 == nil { if err1 = dstFD.pd.waitWrite(dstFD.isFile); err1 == nil {
continue continue
} }
} }
......
...@@ -34,7 +34,7 @@ func SendFile(dstFD *FD, src int, remain int64) (int64, error) { ...@@ -34,7 +34,7 @@ func SendFile(dstFD *FD, src int, remain int64) (int64, error) {
break break
} }
if err1 == syscall.EAGAIN { if err1 == syscall.EAGAIN {
if err1 = dstFD.pd.waitWrite(); err1 == nil { if err1 = dstFD.pd.waitWrite(dstFD.isFile); err1 == nil {
continue continue
} }
} }
......
...@@ -47,7 +47,7 @@ func SendFile(dstFD *FD, src int, pos, remain int64) (int64, error) { ...@@ -47,7 +47,7 @@ func SendFile(dstFD *FD, src int, pos, remain int64) (int64, error) {
break break
} }
if err1 == syscall.EAGAIN { if err1 == syscall.EAGAIN {
if err1 = dstFD.pd.waitWrite(); err1 == nil { if err1 = dstFD.pd.waitWrite(dstFD.isFile); err1 == nil {
continue continue
} }
} }
......
...@@ -18,7 +18,7 @@ func (fd *FD) Writev(v *[][]byte) (int64, error) { ...@@ -18,7 +18,7 @@ func (fd *FD) Writev(v *[][]byte) (int64, error) {
return 0, err return 0, err
} }
defer fd.writeUnlock() defer fd.writeUnlock()
if err := fd.pd.prepareWrite(); err != nil { if err := fd.pd.prepareWrite(fd.isFile); err != nil {
return 0, err return 0, err
} }
...@@ -65,7 +65,7 @@ func (fd *FD) Writev(v *[][]byte) (int64, error) { ...@@ -65,7 +65,7 @@ func (fd *FD) Writev(v *[][]byte) (int64, error) {
n += int64(wrote) n += int64(wrote)
consume(v, int64(wrote)) consume(v, int64(wrote))
if e0 == syscall.EAGAIN { if e0 == syscall.EAGAIN {
if err = fd.pd.waitWrite(); err == nil { if err = fd.pd.waitWrite(fd.isFile); err == nil {
continue continue
} }
} else if e0 != 0 { } else if e0 != 0 {
......
...@@ -13,6 +13,7 @@ import ( ...@@ -13,6 +13,7 @@ import (
"net/internal/socktest" "net/internal/socktest"
"os" "os"
"runtime" "runtime"
"strings"
"testing" "testing"
"time" "time"
) )
...@@ -98,7 +99,7 @@ second: ...@@ -98,7 +99,7 @@ second:
goto third goto third
} }
switch nestedErr { switch nestedErr {
case errCanceled, poll.ErrClosing, errMissingAddress, errNoSuitableAddress, case errCanceled, poll.ErrNetClosing, errMissingAddress, errNoSuitableAddress,
context.DeadlineExceeded, context.Canceled: context.DeadlineExceeded, context.Canceled:
return nil return nil
} }
...@@ -433,7 +434,7 @@ second: ...@@ -433,7 +434,7 @@ second:
goto third goto third
} }
switch nestedErr { switch nestedErr {
case poll.ErrClosing, poll.ErrTimeout: case poll.ErrNetClosing, poll.ErrTimeout:
return nil return nil
} }
return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr) return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
...@@ -475,7 +476,7 @@ second: ...@@ -475,7 +476,7 @@ second:
goto third goto third
} }
switch nestedErr { switch nestedErr {
case errCanceled, poll.ErrClosing, errMissingAddress, poll.ErrTimeout, ErrWriteToConnected, io.ErrUnexpectedEOF: case errCanceled, poll.ErrNetClosing, errMissingAddress, poll.ErrTimeout, ErrWriteToConnected, io.ErrUnexpectedEOF:
return nil return nil
} }
return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr) return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
...@@ -490,11 +491,21 @@ third: ...@@ -490,11 +491,21 @@ third:
// parseCloseError parses nestedErr and reports whether it is a valid // parseCloseError parses nestedErr and reports whether it is a valid
// error value from Close functions. // error value from Close functions.
// It returns nil when nestedErr is valid. // It returns nil when nestedErr is valid.
func parseCloseError(nestedErr error) error { func parseCloseError(nestedErr error, isShutdown bool) error {
if nestedErr == nil { if nestedErr == nil {
return nil return nil
} }
// Because historically we have not exported the error that we
// return for an operation on a closed network connection,
// there are programs that test for the exact error string.
// Verify that string here so that we don't break those
// programs unexpectedly. See issues #4373 and #19252.
want := "use of closed network connection"
if !isShutdown && !strings.Contains(nestedErr.Error(), want) {
return fmt.Errorf("error string %q does not contain expected string %q", nestedErr, want)
}
switch err := nestedErr.(type) { switch err := nestedErr.(type) {
case *OpError: case *OpError:
if err := err.isValid(); err != nil { if err := err.isValid(); err != nil {
...@@ -518,7 +529,7 @@ second: ...@@ -518,7 +529,7 @@ second:
goto third goto third
} }
switch nestedErr { switch nestedErr {
case poll.ErrClosing: case poll.ErrNetClosing:
return nil return nil
} }
return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr) return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
...@@ -548,23 +559,23 @@ func TestCloseError(t *testing.T) { ...@@ -548,23 +559,23 @@ func TestCloseError(t *testing.T) {
for i := 0; i < 3; i++ { for i := 0; i < 3; i++ {
err = c.(*TCPConn).CloseRead() err = c.(*TCPConn).CloseRead()
if perr := parseCloseError(err); perr != nil { if perr := parseCloseError(err, true); perr != nil {
t.Errorf("#%d: %v", i, perr) t.Errorf("#%d: %v", i, perr)
} }
} }
for i := 0; i < 3; i++ { for i := 0; i < 3; i++ {
err = c.(*TCPConn).CloseWrite() err = c.(*TCPConn).CloseWrite()
if perr := parseCloseError(err); perr != nil { if perr := parseCloseError(err, true); perr != nil {
t.Errorf("#%d: %v", i, perr) t.Errorf("#%d: %v", i, perr)
} }
} }
for i := 0; i < 3; i++ { for i := 0; i < 3; i++ {
err = c.Close() err = c.Close()
if perr := parseCloseError(err); perr != nil { if perr := parseCloseError(err, false); perr != nil {
t.Errorf("#%d: %v", i, perr) t.Errorf("#%d: %v", i, perr)
} }
err = ln.Close() err = ln.Close()
if perr := parseCloseError(err); perr != nil { if perr := parseCloseError(err, false); perr != nil {
t.Errorf("#%d: %v", i, perr) t.Errorf("#%d: %v", i, perr)
} }
} }
...@@ -577,7 +588,7 @@ func TestCloseError(t *testing.T) { ...@@ -577,7 +588,7 @@ func TestCloseError(t *testing.T) {
for i := 0; i < 3; i++ { for i := 0; i < 3; i++ {
err = pc.Close() err = pc.Close()
if perr := parseCloseError(err); perr != nil { if perr := parseCloseError(err, false); perr != nil {
t.Errorf("#%d: %v", i, perr) t.Errorf("#%d: %v", i, perr)
} }
} }
...@@ -614,7 +625,7 @@ second: ...@@ -614,7 +625,7 @@ second:
goto third goto third
} }
switch nestedErr { switch nestedErr {
case poll.ErrClosing, poll.ErrTimeout: case poll.ErrNetClosing, poll.ErrTimeout:
return nil return nil
} }
return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr) return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
...@@ -693,7 +704,7 @@ second: ...@@ -693,7 +704,7 @@ second:
goto third goto third
} }
switch nestedErr { switch nestedErr {
case poll.ErrClosing: case poll.ErrNetClosing:
return nil return nil
} }
return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr) return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
......
...@@ -43,7 +43,7 @@ func newFD(sysfd, family, sotype int, net string) (*netFD, error) { ...@@ -43,7 +43,7 @@ func newFD(sysfd, family, sotype int, net string) (*netFD, error) {
} }
func (fd *netFD) init() error { func (fd *netFD) init() error {
return fd.pfd.Init() return fd.pfd.Init(fd.net, true)
} }
func (fd *netFD) setAddr(laddr, raddr Addr) { func (fd *netFD) setAddr(laddr, raddr Addr) {
...@@ -75,7 +75,7 @@ func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (ret erro ...@@ -75,7 +75,7 @@ func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (ret erro
return mapErr(ctx.Err()) return mapErr(ctx.Err())
default: default:
} }
if err := fd.pfd.Init(); err != nil { if err := fd.pfd.Init(fd.net, true); err != nil {
return err return err
} }
runtime.KeepAlive(fd) runtime.KeepAlive(fd)
...@@ -93,7 +93,7 @@ func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (ret erro ...@@ -93,7 +93,7 @@ func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (ret erro
default: default:
return os.NewSyscallError("connect", err) return os.NewSyscallError("connect", err)
} }
if err := fd.pfd.Init(); err != nil { if err := fd.pfd.Init(fd.net, true); err != nil {
return err return err
} }
if deadline, _ := ctx.Deadline(); !deadline.IsZero() { if deadline, _ := ctx.Deadline(); !deadline.IsZero() {
......
...@@ -90,7 +90,7 @@ func TestFileConn(t *testing.T) { ...@@ -90,7 +90,7 @@ func TestFileConn(t *testing.T) {
f, err = c1.File() f, err = c1.File()
} }
if err := c1.Close(); err != nil { if err := c1.Close(); err != nil {
if perr := parseCloseError(err); perr != nil { if perr := parseCloseError(err, false); perr != nil {
t.Error(perr) t.Error(perr)
} }
t.Error(err) t.Error(err)
...@@ -256,7 +256,7 @@ func TestFilePacketConn(t *testing.T) { ...@@ -256,7 +256,7 @@ func TestFilePacketConn(t *testing.T) {
f, err = c1.File() f, err = c1.File()
} }
if err := c1.Close(); err != nil { if err := c1.Close(); err != nil {
if perr := parseCloseError(err); perr != nil { if perr := parseCloseError(err, false); perr != nil {
t.Error(perr) t.Error(perr)
} }
t.Error(err) t.Error(err)
......
...@@ -54,7 +54,7 @@ func TestCloseRead(t *testing.T) { ...@@ -54,7 +54,7 @@ func TestCloseRead(t *testing.T) {
err = c.CloseRead() err = c.CloseRead()
} }
if err != nil { if err != nil {
if perr := parseCloseError(err); perr != nil { if perr := parseCloseError(err, true); perr != nil {
t.Error(perr) t.Error(perr)
} }
t.Fatal(err) t.Fatal(err)
...@@ -94,7 +94,7 @@ func TestCloseWrite(t *testing.T) { ...@@ -94,7 +94,7 @@ func TestCloseWrite(t *testing.T) {
err = c.CloseWrite() err = c.CloseWrite()
} }
if err != nil { if err != nil {
if perr := parseCloseError(err); perr != nil { if perr := parseCloseError(err, true); perr != nil {
t.Error(perr) t.Error(perr)
} }
t.Error(err) t.Error(err)
...@@ -139,7 +139,7 @@ func TestCloseWrite(t *testing.T) { ...@@ -139,7 +139,7 @@ func TestCloseWrite(t *testing.T) {
err = c.CloseWrite() err = c.CloseWrite()
} }
if err != nil { if err != nil {
if perr := parseCloseError(err); perr != nil { if perr := parseCloseError(err, true); perr != nil {
t.Error(perr) t.Error(perr)
} }
t.Fatal(err) t.Fatal(err)
...@@ -184,7 +184,7 @@ func TestConnClose(t *testing.T) { ...@@ -184,7 +184,7 @@ func TestConnClose(t *testing.T) {
defer c.Close() defer c.Close()
if err := c.Close(); err != nil { if err := c.Close(); err != nil {
if perr := parseCloseError(err); perr != nil { if perr := parseCloseError(err, false); perr != nil {
t.Error(perr) t.Error(perr)
} }
t.Fatal(err) t.Fatal(err)
...@@ -215,7 +215,7 @@ func TestListenerClose(t *testing.T) { ...@@ -215,7 +215,7 @@ func TestListenerClose(t *testing.T) {
dst := ln.Addr().String() dst := ln.Addr().String()
if err := ln.Close(); err != nil { if err := ln.Close(); err != nil {
if perr := parseCloseError(err); perr != nil { if perr := parseCloseError(err, false); perr != nil {
t.Error(perr) t.Error(perr)
} }
t.Fatal(err) t.Fatal(err)
...@@ -269,7 +269,7 @@ func TestPacketConnClose(t *testing.T) { ...@@ -269,7 +269,7 @@ func TestPacketConnClose(t *testing.T) {
defer c.Close() defer c.Close()
if err := c.Close(); err != nil { if err := c.Close(); err != nil {
if perr := parseCloseError(err); perr != nil { if perr := parseCloseError(err, false); perr != nil {
t.Error(perr) t.Error(perr)
} }
t.Fatal(err) t.Fatal(err)
...@@ -292,7 +292,7 @@ func TestListenCloseListen(t *testing.T) { ...@@ -292,7 +292,7 @@ func TestListenCloseListen(t *testing.T) {
} }
addr := ln.Addr().String() addr := ln.Addr().String()
if err := ln.Close(); err != nil { if err := ln.Close(); err != nil {
if perr := parseCloseError(err); perr != nil { if perr := parseCloseError(err, false); perr != nil {
t.Error(perr) t.Error(perr)
} }
t.Fatal(err) t.Fatal(err)
......
...@@ -102,7 +102,7 @@ func (f *File) Read(b []byte) (n int, err error) { ...@@ -102,7 +102,7 @@ func (f *File) Read(b []byte) (n int, err error) {
} }
n, e := f.read(b) n, e := f.read(b)
if e != nil { if e != nil {
if e == poll.ErrClosing { if e == poll.ErrFileClosing {
e = ErrClosed e = ErrClosed
} }
if e == io.EOF { if e == io.EOF {
......
...@@ -87,20 +87,18 @@ func newFile(fd uintptr, name string, pollable bool) *File { ...@@ -87,20 +87,18 @@ func newFile(fd uintptr, name string, pollable bool) *File {
pollable = false pollable = false
} }
if pollable { if err := f.pfd.Init("file", pollable); err != nil {
if err := f.pfd.Init(); err != nil { // An error here indicates a failure to register
// An error here indicates a failure to register // with the netpoll system. That can happen for
// with the netpoll system. That can happen for // a file descriptor that is not supported by
// a file descriptor that is not supported by // epoll/kqueue; for example, disk files on
// epoll/kqueue; for example, disk files on // GNU/Linux systems. We assume that any real error
// GNU/Linux systems. We assume that any real error // will show up in later I/O.
// will show up in later I/O. } else if pollable {
} else { // We successfully registered with netpoll, so put
// We successfully registered with netpoll, so put // the file into nonblocking mode.
// the file into nonblocking mode. if err := syscall.SetNonblock(fdi, true); err == nil {
if err := syscall.SetNonblock(fdi, true); err == nil { f.nonblock = true
f.nonblock = true
}
} }
} }
......
...@@ -144,6 +144,8 @@ func TestClosedPipeRace(t *testing.T) { ...@@ -144,6 +144,8 @@ func TestClosedPipeRace(t *testing.T) {
t.Error("Read of closed pipe unexpectedly succeeded") t.Error("Read of closed pipe unexpectedly succeeded")
} else if pe, ok := err.(*os.PathError); !ok { } else if pe, ok := err.(*os.PathError); !ok {
t.Errorf("Read of closed pipe returned unexpected error type %T; expected os.PathError", pe) t.Errorf("Read of closed pipe returned unexpected error type %T; expected os.PathError", pe)
} else if pe.Err != os.ErrClosed {
t.Errorf("got error %q but expected %q", pe.Err, os.ErrClosed)
} else { } else {
t.Logf("Read returned expected error %q", err) t.Logf("Read returned expected error %q", err)
} }
......
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