mirror of https://github.com/golang/go.git
os, net: define and use os.ErrDeadlineExceeded
If an I/O operation fails because a deadline was exceeded, return os.ErrDeadlineExceeded. We used to return poll.ErrTimeout, an internal error, and told users to check the Timeout method. However, there are other errors with a Timeout method that returns true, notably syscall.ETIMEDOUT which is returned for a keep-alive timeout. Checking errors.Is(err, os.ErrDeadlineExceeded) should permit code to reliably tell why it failed. This change does not affect the handling of net.Dialer.Deadline, nor does it change the handling of net.DialContext when the context deadline is exceeded. Those cases continue to return an error reported as "i/o timeout" for which Timeout is true, but that error is not os.ErrDeadlineExceeded. Fixes #31449 Change-Id: I0323f42e944324c6f2578f00c3ac90c24fe81177 Reviewed-on: https://go-review.googlesource.com/c/go/+/228645 Run-TryBot: Ian Lance Taylor <iant@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Filippo Valsorda <filippo@golang.org>
This commit is contained in:
parent
396833caef
commit
d422f54619
|
|
@ -172,6 +172,25 @@ TODO
|
|||
</dd>
|
||||
</dl>
|
||||
|
||||
<dl id="net"><dt><a href="/pkg/net/">net</a></dt>
|
||||
<dd>
|
||||
<p><!-- CL -->
|
||||
If an I/O operation exceeds a deadline set by
|
||||
the <a href="/pkg/net/#Conn"><code>Conn.SetDeadline</code></a>,
|
||||
<code>Conn.SetReadDeadline</code>,
|
||||
or <code>Conn.SetWriteDeadline</code> methods, it will now
|
||||
return an error that is or wraps
|
||||
<a href="/pkg/os#ErrDeadlineExceeded"><code>os.ErrDeadlineExceeded</code></a>.
|
||||
This may be used to reliably detect whether an error is due to
|
||||
an exceeded deadline.
|
||||
Earlier releases recommended calling the <code>Timeout</code>
|
||||
method on the error, but I/O operations can return errors for
|
||||
which <code>Timeout</code> returns <code>true</code> although a
|
||||
deadline has not been exceeded.
|
||||
</p>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<dl id="net/http/pprof"><dt><a href="/pkg/net/http/pprof/">net/http/pprof</a></dt>
|
||||
<dd>
|
||||
<p><!-- CL 147598, 229537 -->
|
||||
|
|
@ -200,6 +219,25 @@ TODO
|
|||
</dd>
|
||||
</dl>
|
||||
|
||||
<dl id="os"><dt><a href="/pkg/os/">os</a></dt>
|
||||
<dd>
|
||||
<p><!-- CL -->
|
||||
If an I/O operation exceeds a deadline set by
|
||||
the <a href="/pkg/os/#File.SetDeadline"><code>File.SetDeadline</code></a>,
|
||||
<a href="/pkg/os/#File.SetReadDeadline"><code>File.SetReadDeadline</code></a>,
|
||||
or <a href="/pkg/os/#File.SetWriteDeadline"><code>File.SetWriteDeadline</code></a>
|
||||
methods, it will now return an error that is or wraps
|
||||
<a href="/pkg/os#ErrDeadlineExceeded"><code>os.ErrDeadlineExceeded</code></a>.
|
||||
This may be used to reliably detect whether an error is due to
|
||||
an exceeded deadline.
|
||||
Earlier releases recommended calling the <code>Timeout</code>
|
||||
method on the error, but I/O operations can return errors for
|
||||
which <code>Timeout</code> returns <code>true</code> although a
|
||||
deadline has not been exceeded.
|
||||
</p>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<dl id="reflect"><dt><a href="/pkg/reflect/">reflect</a></dt>
|
||||
<dd>
|
||||
<p><!-- CL 228902 -->
|
||||
|
|
|
|||
|
|
@ -35,16 +35,20 @@ func errClosing(isFile bool) error {
|
|||
return ErrNetClosing
|
||||
}
|
||||
|
||||
// ErrTimeout is returned for an expired deadline.
|
||||
var ErrTimeout error = &TimeoutError{}
|
||||
// ErrDeadlineExceeded is returned for an expired deadline.
|
||||
// This is exported by the os package as os.ErrDeadlineExceeded.
|
||||
var ErrDeadlineExceeded error = &DeadlineExceededError{}
|
||||
|
||||
// TimeoutError is returned for an expired deadline.
|
||||
type TimeoutError struct{}
|
||||
// DeadlineExceededError is returned for an expired deadline.
|
||||
type DeadlineExceededError struct{}
|
||||
|
||||
// Implement the net.Error interface.
|
||||
func (e *TimeoutError) Error() string { return "i/o timeout" }
|
||||
func (e *TimeoutError) Timeout() bool { return true }
|
||||
func (e *TimeoutError) Temporary() bool { return true }
|
||||
// The string is "i/o timeout" because that is what was returned
|
||||
// by earlier Go versions. Changing it may break programs that
|
||||
// match on error strings.
|
||||
func (e *DeadlineExceededError) Error() string { return "i/o timeout" }
|
||||
func (e *DeadlineExceededError) Timeout() bool { return true }
|
||||
func (e *DeadlineExceededError) Temporary() bool { return true }
|
||||
|
||||
// ErrNotPollable is returned when the file or socket is not suitable
|
||||
// for event notification.
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ func (fd *FD) Close() error {
|
|||
// Read implements io.Reader.
|
||||
func (fd *FD) Read(fn func([]byte) (int, error), b []byte) (int, error) {
|
||||
if fd.rtimedout.isSet() {
|
||||
return 0, ErrTimeout
|
||||
return 0, ErrDeadlineExceeded
|
||||
}
|
||||
if err := fd.readLock(); err != nil {
|
||||
return 0, err
|
||||
|
|
@ -76,7 +76,7 @@ func (fd *FD) Read(fn func([]byte) (int, error), b []byte) (int, error) {
|
|||
err = io.EOF
|
||||
}
|
||||
if isInterrupted(err) {
|
||||
err = ErrTimeout
|
||||
err = ErrDeadlineExceeded
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
|
@ -84,7 +84,7 @@ func (fd *FD) Read(fn func([]byte) (int, error), b []byte) (int, error) {
|
|||
// Write implements io.Writer.
|
||||
func (fd *FD) Write(fn func([]byte) (int, error), b []byte) (int, error) {
|
||||
if fd.wtimedout.isSet() {
|
||||
return 0, ErrTimeout
|
||||
return 0, ErrDeadlineExceeded
|
||||
}
|
||||
if err := fd.writeLock(); err != nil {
|
||||
return 0, err
|
||||
|
|
@ -94,7 +94,7 @@ func (fd *FD) Write(fn func([]byte) (int, error), b []byte) (int, error) {
|
|||
n, err := fd.waio.Wait()
|
||||
fd.waio = nil
|
||||
if isInterrupted(err) {
|
||||
err = ErrTimeout
|
||||
err = ErrDeadlineExceeded
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ func (pd *pollDesc) wait(mode int, isFile bool) error {
|
|||
if isFile { // TODO(neelance): wasm: Use callbacks from JS to block until the read/write finished.
|
||||
return nil
|
||||
}
|
||||
return ErrTimeout
|
||||
return ErrDeadlineExceeded
|
||||
}
|
||||
|
||||
func (pd *pollDesc) waitRead(isFile bool) error { return pd.wait('r', isFile) }
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ func convertErr(res int, isFile bool) error {
|
|||
case pollErrClosing:
|
||||
return errClosing(isFile)
|
||||
case pollErrTimeout:
|
||||
return ErrTimeout
|
||||
return ErrDeadlineExceeded
|
||||
case pollErrNotPollable:
|
||||
return ErrNotPollable
|
||||
}
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ func execIO(o *operation, submit func(o *operation) error) (int, error) {
|
|||
// IO is interrupted by "close" or "timeout"
|
||||
netpollErr := err
|
||||
switch netpollErr {
|
||||
case ErrNetClosing, ErrFileClosing, ErrTimeout:
|
||||
case ErrNetClosing, ErrFileClosing, ErrDeadlineExceeded:
|
||||
// will deal with those.
|
||||
default:
|
||||
panic("unexpected runtime.netpoll error: " + netpollErr.Error())
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ package net
|
|||
import (
|
||||
"context"
|
||||
"internal/nettrace"
|
||||
"internal/poll"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
|
@ -141,7 +140,7 @@ func partialDeadline(now, deadline time.Time, addrsRemaining int) (time.Time, er
|
|||
}
|
||||
timeRemaining := deadline.Sub(now)
|
||||
if timeRemaining <= 0 {
|
||||
return time.Time{}, poll.ErrTimeout
|
||||
return time.Time{}, errTimeout
|
||||
}
|
||||
// Tentatively allocate equal time to each remaining address.
|
||||
timeout := timeRemaining / time.Duration(addrsRemaining)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ package net
|
|||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"internal/poll"
|
||||
"internal/testenv"
|
||||
"io"
|
||||
"os"
|
||||
|
|
@ -540,8 +539,8 @@ func TestDialerPartialDeadline(t *testing.T) {
|
|||
{now, noDeadline, 1, noDeadline, nil},
|
||||
// Step the clock forward and cross the deadline.
|
||||
{now.Add(-1 * time.Millisecond), now, 1, now, nil},
|
||||
{now.Add(0 * time.Millisecond), now, 1, noDeadline, poll.ErrTimeout},
|
||||
{now.Add(1 * time.Millisecond), now, 1, noDeadline, poll.ErrTimeout},
|
||||
{now.Add(0 * time.Millisecond), now, 1, noDeadline, errTimeout},
|
||||
{now.Add(1 * time.Millisecond), now, 1, noDeadline, errTimeout},
|
||||
}
|
||||
for i, tt := range testCases {
|
||||
deadline, err := partialDeadline(tt.now, tt.deadline, tt.addrs)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"internal/poll"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
|
|
@ -480,7 +479,7 @@ func TestGoLookupIPWithResolverConfig(t *testing.T) {
|
|||
break
|
||||
default:
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
return dnsmessage.Message{}, poll.ErrTimeout
|
||||
return dnsmessage.Message{}, os.ErrDeadlineExceeded
|
||||
}
|
||||
r := dnsmessage.Message{
|
||||
Header: dnsmessage.Header{
|
||||
|
|
@ -993,7 +992,7 @@ func TestRetryTimeout(t *testing.T) {
|
|||
if s == "192.0.2.1:53" {
|
||||
deadline0 = deadline
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
return dnsmessage.Message{}, poll.ErrTimeout
|
||||
return dnsmessage.Message{}, os.ErrDeadlineExceeded
|
||||
}
|
||||
|
||||
if deadline.Equal(deadline0) {
|
||||
|
|
@ -1131,7 +1130,7 @@ func TestStrictErrorsLookupIP(t *testing.T) {
|
|||
}
|
||||
makeTimeout := func() error {
|
||||
return &DNSError{
|
||||
Err: poll.ErrTimeout.Error(),
|
||||
Err: os.ErrDeadlineExceeded.Error(),
|
||||
Name: name,
|
||||
Server: server,
|
||||
IsTimeout: true,
|
||||
|
|
@ -1247,7 +1246,7 @@ func TestStrictErrorsLookupIP(t *testing.T) {
|
|||
Questions: q.Questions,
|
||||
}, nil
|
||||
case resolveTimeout:
|
||||
return dnsmessage.Message{}, poll.ErrTimeout
|
||||
return dnsmessage.Message{}, os.ErrDeadlineExceeded
|
||||
default:
|
||||
t.Fatal("Impossible resolveWhich")
|
||||
}
|
||||
|
|
@ -1372,7 +1371,7 @@ func TestStrictErrorsLookupTXT(t *testing.T) {
|
|||
|
||||
switch q.Questions[0].Name.String() {
|
||||
case searchX:
|
||||
return dnsmessage.Message{}, poll.ErrTimeout
|
||||
return dnsmessage.Message{}, os.ErrDeadlineExceeded
|
||||
case searchY:
|
||||
return mockTXTResponse(q), nil
|
||||
default:
|
||||
|
|
@ -1387,7 +1386,7 @@ func TestStrictErrorsLookupTXT(t *testing.T) {
|
|||
var wantRRs int
|
||||
if strict {
|
||||
wantErr = &DNSError{
|
||||
Err: poll.ErrTimeout.Error(),
|
||||
Err: os.ErrDeadlineExceeded.Error(),
|
||||
Name: name,
|
||||
Server: server,
|
||||
IsTimeout: true,
|
||||
|
|
@ -1415,7 +1414,7 @@ func TestDNSGoroutineRace(t *testing.T) {
|
|||
|
||||
fake := fakeDNSServer{rh: func(n, s string, q dnsmessage.Message, t time.Time) (dnsmessage.Message, error) {
|
||||
time.Sleep(10 * time.Microsecond)
|
||||
return dnsmessage.Message{}, poll.ErrTimeout
|
||||
return dnsmessage.Message{}, os.ErrDeadlineExceeded
|
||||
}}
|
||||
r := Resolver{PreferGo: true, Dial: fake.DialContext}
|
||||
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ second:
|
|||
return nil
|
||||
}
|
||||
switch err := nestedErr.(type) {
|
||||
case *AddrError, addrinfoErrno, *DNSError, InvalidAddrError, *ParseError, *poll.TimeoutError, UnknownNetworkError:
|
||||
case *AddrError, addrinfoErrno, *timeoutError, *DNSError, InvalidAddrError, *ParseError, *poll.DeadlineExceededError, UnknownNetworkError:
|
||||
return nil
|
||||
case *os.SyscallError:
|
||||
nestedErr = err.Err
|
||||
|
|
@ -436,7 +436,7 @@ second:
|
|||
goto third
|
||||
}
|
||||
switch nestedErr {
|
||||
case poll.ErrNetClosing, poll.ErrTimeout, poll.ErrNotPollable:
|
||||
case poll.ErrNetClosing, errTimeout, poll.ErrNotPollable, os.ErrDeadlineExceeded:
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
|
||||
|
|
@ -471,14 +471,14 @@ second:
|
|||
return nil
|
||||
}
|
||||
switch err := nestedErr.(type) {
|
||||
case *AddrError, addrinfoErrno, *DNSError, InvalidAddrError, *ParseError, *poll.TimeoutError, UnknownNetworkError:
|
||||
case *AddrError, addrinfoErrno, *timeoutError, *DNSError, InvalidAddrError, *ParseError, *poll.DeadlineExceededError, UnknownNetworkError:
|
||||
return nil
|
||||
case *os.SyscallError:
|
||||
nestedErr = err.Err
|
||||
goto third
|
||||
}
|
||||
switch nestedErr {
|
||||
case errCanceled, poll.ErrNetClosing, errMissingAddress, poll.ErrTimeout, ErrWriteToConnected, io.ErrUnexpectedEOF:
|
||||
case errCanceled, poll.ErrNetClosing, errMissingAddress, errTimeout, os.ErrDeadlineExceeded, ErrWriteToConnected, io.ErrUnexpectedEOF:
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
|
||||
|
|
@ -627,7 +627,7 @@ second:
|
|||
goto third
|
||||
}
|
||||
switch nestedErr {
|
||||
case poll.ErrNetClosing, poll.ErrTimeout, poll.ErrNotPollable:
|
||||
case poll.ErrNetClosing, errTimeout, poll.ErrNotPollable, os.ErrDeadlineExceeded:
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("unexpected type on 2nd nested level: %T", nestedErr)
|
||||
|
|
|
|||
|
|
@ -81,7 +81,6 @@ package net
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"internal/poll"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
|
|
@ -136,23 +135,22 @@ type Conn interface {
|
|||
// SetReadDeadline and SetWriteDeadline.
|
||||
//
|
||||
// A deadline is an absolute time after which I/O operations
|
||||
// fail with a timeout (see type Error) instead of
|
||||
// blocking. The deadline applies to all future and pending
|
||||
// I/O, not just the immediately following call to Read or
|
||||
// Write. After a deadline has been exceeded, the connection
|
||||
// can be refreshed by setting a deadline in the future.
|
||||
// fail instead of blocking. The deadline applies to all future
|
||||
// and pending I/O, not just the immediately following call to
|
||||
// Read or Write. After a deadline has been exceeded, the
|
||||
// connection can be refreshed by setting a deadline in the future.
|
||||
//
|
||||
// If the deadline is exceeded a call to Read or Write or to other
|
||||
// I/O methods will return an error that wraps os.ErrDeadlineExceeded.
|
||||
// This can be tested using errors.Is(err, os.ErrDeadlineExceeded).
|
||||
// The error's Timeout method will return true, but note that there
|
||||
// are other possible errors for which the Timeout method will
|
||||
// return true even if the deadline has not been exceeded.
|
||||
//
|
||||
// An idle timeout can be implemented by repeatedly extending
|
||||
// the deadline after successful Read or Write calls.
|
||||
//
|
||||
// A zero value for t means I/O operations will not time out.
|
||||
//
|
||||
// Note that if a TCP connection has keep-alive turned on,
|
||||
// which is the default unless overridden by Dialer.KeepAlive
|
||||
// or ListenConfig.KeepAlive, then a keep-alive failure may
|
||||
// also return a timeout error. On Unix systems a keep-alive
|
||||
// failure on I/O can be detected using
|
||||
// errors.Is(err, syscall.ETIMEDOUT).
|
||||
SetDeadline(t time.Time) error
|
||||
|
||||
// SetReadDeadline sets the deadline for future Read calls
|
||||
|
|
@ -420,7 +418,7 @@ func mapErr(err error) error {
|
|||
case context.Canceled:
|
||||
return errCanceled
|
||||
case context.DeadlineExceeded:
|
||||
return poll.ErrTimeout
|
||||
return errTimeout
|
||||
default:
|
||||
return err
|
||||
}
|
||||
|
|
@ -567,6 +565,21 @@ func (e InvalidAddrError) Error() string { return string(e) }
|
|||
func (e InvalidAddrError) Timeout() bool { return false }
|
||||
func (e InvalidAddrError) Temporary() bool { return false }
|
||||
|
||||
// errTimeout exists to return the historical "i/o timeout" string
|
||||
// for context.DeadlineExceeded. See mapErr.
|
||||
// It is also used when Dialer.Deadline is exceeded.
|
||||
//
|
||||
// TODO(iant): We could consider changing this to os.ErrDeadlineExceeded
|
||||
// in the future, but note that that would conflict with the TODO
|
||||
// at mapErr that suggests changing it to context.DeadlineExceeded.
|
||||
var errTimeout error = &timeoutError{}
|
||||
|
||||
type timeoutError struct{}
|
||||
|
||||
func (e *timeoutError) Error() string { return "i/o timeout" }
|
||||
func (e *timeoutError) Timeout() bool { return true }
|
||||
func (e *timeoutError) Temporary() bool { return true }
|
||||
|
||||
// DNSConfigError represents an error reading the machine's DNS configuration.
|
||||
// (No longer used; kept for compatibility.)
|
||||
type DNSConfigError struct {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ package net
|
|||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
|
@ -78,12 +79,6 @@ func isClosedChan(c <-chan struct{}) bool {
|
|||
}
|
||||
}
|
||||
|
||||
type timeoutError struct{}
|
||||
|
||||
func (timeoutError) Error() string { return "deadline exceeded" }
|
||||
func (timeoutError) Timeout() bool { return true }
|
||||
func (timeoutError) Temporary() bool { return true }
|
||||
|
||||
type pipeAddr struct{}
|
||||
|
||||
func (pipeAddr) Network() string { return "pipe" }
|
||||
|
|
@ -158,7 +153,7 @@ func (p *pipe) read(b []byte) (n int, err error) {
|
|||
case isClosedChan(p.remoteDone):
|
||||
return 0, io.EOF
|
||||
case isClosedChan(p.readDeadline.wait()):
|
||||
return 0, timeoutError{}
|
||||
return 0, os.ErrDeadlineExceeded
|
||||
}
|
||||
|
||||
select {
|
||||
|
|
@ -171,7 +166,7 @@ func (p *pipe) read(b []byte) (n int, err error) {
|
|||
case <-p.remoteDone:
|
||||
return 0, io.EOF
|
||||
case <-p.readDeadline.wait():
|
||||
return 0, timeoutError{}
|
||||
return 0, os.ErrDeadlineExceeded
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -190,7 +185,7 @@ func (p *pipe) write(b []byte) (n int, err error) {
|
|||
case isClosedChan(p.remoteDone):
|
||||
return 0, io.ErrClosedPipe
|
||||
case isClosedChan(p.writeDeadline.wait()):
|
||||
return 0, timeoutError{}
|
||||
return 0, os.ErrDeadlineExceeded
|
||||
}
|
||||
|
||||
p.wrMu.Lock() // Ensure entirety of b is written together
|
||||
|
|
@ -206,7 +201,7 @@ func (p *pipe) write(b []byte) (n int, err error) {
|
|||
case <-p.remoteDone:
|
||||
return n, io.ErrClosedPipe
|
||||
case <-p.writeDeadline.wait():
|
||||
return n, timeoutError{}
|
||||
return n, os.ErrDeadlineExceeded
|
||||
}
|
||||
}
|
||||
return n, nil
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ func TestRawConnReadWrite(t *testing.T) {
|
|||
if perr := parseWriteError(err); perr != nil {
|
||||
t.Error(perr)
|
||||
}
|
||||
if nerr, ok := err.(Error); !ok || !nerr.Timeout() {
|
||||
if !isDeadlineExceeded(err) {
|
||||
t.Errorf("got %v; want timeout", err)
|
||||
}
|
||||
if _, err = readRawConn(cc, b[:]); err == nil {
|
||||
|
|
@ -139,7 +139,7 @@ func TestRawConnReadWrite(t *testing.T) {
|
|||
if perr := parseReadError(err); perr != nil {
|
||||
t.Error(perr)
|
||||
}
|
||||
if nerr, ok := err.(Error); !ok || !nerr.Timeout() {
|
||||
if !isDeadlineExceeded(err) {
|
||||
t.Errorf("got %v; want timeout", err)
|
||||
}
|
||||
|
||||
|
|
@ -153,7 +153,7 @@ func TestRawConnReadWrite(t *testing.T) {
|
|||
if perr := parseReadError(err); perr != nil {
|
||||
t.Error(perr)
|
||||
}
|
||||
if nerr, ok := err.(Error); !ok || !nerr.Timeout() {
|
||||
if !isDeadlineExceeded(err) {
|
||||
t.Errorf("got %v; want timeout", err)
|
||||
}
|
||||
|
||||
|
|
@ -167,7 +167,7 @@ func TestRawConnReadWrite(t *testing.T) {
|
|||
if perr := parseWriteError(err); perr != nil {
|
||||
t.Error(perr)
|
||||
}
|
||||
if nerr, ok := err.(Error); !ok || !nerr.Timeout() {
|
||||
if !isDeadlineExceeded(err) {
|
||||
t.Errorf("got %v; want timeout", err)
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -7,12 +7,13 @@
|
|||
package net
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"internal/poll"
|
||||
"internal/testenv"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/internal/socktest"
|
||||
"os"
|
||||
"runtime"
|
||||
"sync"
|
||||
"testing"
|
||||
|
|
@ -148,9 +149,9 @@ var acceptTimeoutTests = []struct {
|
|||
}{
|
||||
// Tests that accept deadlines in the past work, even if
|
||||
// there's incoming connections available.
|
||||
{-5 * time.Second, [2]error{poll.ErrTimeout, poll.ErrTimeout}},
|
||||
{-5 * time.Second, [2]error{os.ErrDeadlineExceeded, os.ErrDeadlineExceeded}},
|
||||
|
||||
{50 * time.Millisecond, [2]error{nil, poll.ErrTimeout}},
|
||||
{50 * time.Millisecond, [2]error{nil, os.ErrDeadlineExceeded}},
|
||||
}
|
||||
|
||||
func TestAcceptTimeout(t *testing.T) {
|
||||
|
|
@ -194,7 +195,7 @@ func TestAcceptTimeout(t *testing.T) {
|
|||
if perr := parseAcceptError(err); perr != nil {
|
||||
t.Errorf("#%d/%d: %v", i, j, perr)
|
||||
}
|
||||
if nerr, ok := err.(Error); !ok || !nerr.Timeout() {
|
||||
if !isDeadlineExceeded(err) {
|
||||
t.Fatalf("#%d/%d: %v", i, j, err)
|
||||
}
|
||||
}
|
||||
|
|
@ -250,7 +251,7 @@ func TestAcceptTimeoutMustReturn(t *testing.T) {
|
|||
if perr := parseAcceptError(err); perr != nil {
|
||||
t.Error(perr)
|
||||
}
|
||||
if nerr, ok := err.(Error); !ok || !nerr.Timeout() {
|
||||
if !isDeadlineExceeded(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
|
@ -302,9 +303,9 @@ var readTimeoutTests = []struct {
|
|||
}{
|
||||
// Tests that read deadlines work, even if there's data ready
|
||||
// to be read.
|
||||
{-5 * time.Second, [2]error{poll.ErrTimeout, poll.ErrTimeout}},
|
||||
{-5 * time.Second, [2]error{os.ErrDeadlineExceeded, os.ErrDeadlineExceeded}},
|
||||
|
||||
{50 * time.Millisecond, [2]error{nil, poll.ErrTimeout}},
|
||||
{50 * time.Millisecond, [2]error{nil, os.ErrDeadlineExceeded}},
|
||||
}
|
||||
|
||||
func TestReadTimeout(t *testing.T) {
|
||||
|
|
@ -344,7 +345,7 @@ func TestReadTimeout(t *testing.T) {
|
|||
if perr := parseReadError(err); perr != nil {
|
||||
t.Errorf("#%d/%d: %v", i, j, perr)
|
||||
}
|
||||
if nerr, ok := err.(Error); !ok || !nerr.Timeout() {
|
||||
if !isDeadlineExceeded(err) {
|
||||
t.Fatalf("#%d/%d: %v", i, j, err)
|
||||
}
|
||||
}
|
||||
|
|
@ -423,9 +424,9 @@ var readFromTimeoutTests = []struct {
|
|||
}{
|
||||
// Tests that read deadlines work, even if there's data ready
|
||||
// to be read.
|
||||
{-5 * time.Second, [2]error{poll.ErrTimeout, poll.ErrTimeout}},
|
||||
{-5 * time.Second, [2]error{os.ErrDeadlineExceeded, os.ErrDeadlineExceeded}},
|
||||
|
||||
{50 * time.Millisecond, [2]error{nil, poll.ErrTimeout}},
|
||||
{50 * time.Millisecond, [2]error{nil, os.ErrDeadlineExceeded}},
|
||||
}
|
||||
|
||||
func TestReadFromTimeout(t *testing.T) {
|
||||
|
|
@ -468,7 +469,7 @@ func TestReadFromTimeout(t *testing.T) {
|
|||
if perr := parseReadError(err); perr != nil {
|
||||
t.Errorf("#%d/%d: %v", i, j, perr)
|
||||
}
|
||||
if nerr, ok := err.(Error); !ok || !nerr.Timeout() {
|
||||
if !isDeadlineExceeded(err) {
|
||||
t.Fatalf("#%d/%d: %v", i, j, err)
|
||||
}
|
||||
}
|
||||
|
|
@ -491,9 +492,9 @@ var writeTimeoutTests = []struct {
|
|||
}{
|
||||
// Tests that write deadlines work, even if there's buffer
|
||||
// space available to write.
|
||||
{-5 * time.Second, [2]error{poll.ErrTimeout, poll.ErrTimeout}},
|
||||
{-5 * time.Second, [2]error{os.ErrDeadlineExceeded, os.ErrDeadlineExceeded}},
|
||||
|
||||
{10 * time.Millisecond, [2]error{nil, poll.ErrTimeout}},
|
||||
{10 * time.Millisecond, [2]error{nil, os.ErrDeadlineExceeded}},
|
||||
}
|
||||
|
||||
func TestWriteTimeout(t *testing.T) {
|
||||
|
|
@ -522,7 +523,7 @@ func TestWriteTimeout(t *testing.T) {
|
|||
if perr := parseWriteError(err); perr != nil {
|
||||
t.Errorf("#%d/%d: %v", i, j, perr)
|
||||
}
|
||||
if nerr, ok := err.(Error); !ok || !nerr.Timeout() {
|
||||
if !isDeadlineExceeded(err) {
|
||||
t.Fatalf("#%d/%d: %v", i, j, err)
|
||||
}
|
||||
}
|
||||
|
|
@ -605,9 +606,9 @@ var writeToTimeoutTests = []struct {
|
|||
}{
|
||||
// Tests that write deadlines work, even if there's buffer
|
||||
// space available to write.
|
||||
{-5 * time.Second, [2]error{poll.ErrTimeout, poll.ErrTimeout}},
|
||||
{-5 * time.Second, [2]error{os.ErrDeadlineExceeded, os.ErrDeadlineExceeded}},
|
||||
|
||||
{10 * time.Millisecond, [2]error{nil, poll.ErrTimeout}},
|
||||
{10 * time.Millisecond, [2]error{nil, os.ErrDeadlineExceeded}},
|
||||
}
|
||||
|
||||
func TestWriteToTimeout(t *testing.T) {
|
||||
|
|
@ -641,7 +642,7 @@ func TestWriteToTimeout(t *testing.T) {
|
|||
if perr := parseWriteError(err); perr != nil {
|
||||
t.Errorf("#%d/%d: %v", i, j, perr)
|
||||
}
|
||||
if nerr, ok := err.(Error); !ok || !nerr.Timeout() {
|
||||
if !isDeadlineExceeded(err) {
|
||||
t.Fatalf("#%d/%d: %v", i, j, err)
|
||||
}
|
||||
}
|
||||
|
|
@ -685,7 +686,7 @@ func TestReadTimeoutFluctuation(t *testing.T) {
|
|||
if perr := parseReadError(err); perr != nil {
|
||||
t.Error(perr)
|
||||
}
|
||||
if nerr, ok := err.(Error); !ok || !nerr.Timeout() {
|
||||
if !isDeadlineExceeded(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
|
@ -718,7 +719,7 @@ func TestReadFromTimeoutFluctuation(t *testing.T) {
|
|||
if perr := parseReadError(err); perr != nil {
|
||||
t.Error(perr)
|
||||
}
|
||||
if nerr, ok := err.(Error); !ok || !nerr.Timeout() {
|
||||
if !isDeadlineExceeded(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
|
@ -760,7 +761,7 @@ func TestWriteTimeoutFluctuation(t *testing.T) {
|
|||
if perr := parseWriteError(err); perr != nil {
|
||||
t.Error(perr)
|
||||
}
|
||||
if nerr, ok := err.(Error); !ok || !nerr.Timeout() {
|
||||
if !isDeadlineExceeded(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
|
@ -1073,3 +1074,20 @@ func TestConcurrentSetDeadline(t *testing.T) {
|
|||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// isDeadlineExceeded reports whether err is or wraps os.ErrDeadlineExceeded.
|
||||
// We also check that the error implements net.Error, and that the
|
||||
// Timeout method returns true.
|
||||
func isDeadlineExceeded(err error) bool {
|
||||
nerr, ok := err.(Error)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if !nerr.Timeout() {
|
||||
return false
|
||||
}
|
||||
if !errors.Is(err, os.ErrDeadlineExceeded) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ func TestUnixgramZeroBytePayload(t *testing.T) {
|
|||
t.Fatalf("unexpected peer address: %v", peer)
|
||||
}
|
||||
default: // Read may timeout, it depends on the platform
|
||||
if nerr, ok := err.(Error); !ok || !nerr.Timeout() {
|
||||
if !isDeadlineExceeded(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
|
@ -163,7 +163,7 @@ func TestUnixgramZeroByteBuffer(t *testing.T) {
|
|||
t.Fatalf("unexpected peer address: %v", peer)
|
||||
}
|
||||
default: // Read may timeout, it depends on the platform
|
||||
if nerr, ok := err.(Error); !ok || !nerr.Timeout() {
|
||||
if !isDeadlineExceeded(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,11 +18,12 @@ var (
|
|||
// Methods on File will return this error when the receiver is nil.
|
||||
ErrInvalid = errInvalid() // "invalid argument"
|
||||
|
||||
ErrPermission = errPermission() // "permission denied"
|
||||
ErrExist = errExist() // "file already exists"
|
||||
ErrNotExist = errNotExist() // "file does not exist"
|
||||
ErrClosed = errClosed() // "file already closed"
|
||||
ErrNoDeadline = errNoDeadline() // "file type does not support deadline"
|
||||
ErrPermission = errPermission() // "permission denied"
|
||||
ErrExist = errExist() // "file already exists"
|
||||
ErrNotExist = errNotExist() // "file does not exist"
|
||||
ErrClosed = errClosed() // "file already closed"
|
||||
ErrNoDeadline = errNoDeadline() // "file type does not support deadline"
|
||||
ErrDeadlineExceeded = errDeadlineExceeded() // "i/o timeout"
|
||||
)
|
||||
|
||||
func errInvalid() error { return oserror.ErrInvalid }
|
||||
|
|
@ -32,6 +33,15 @@ func errNotExist() error { return oserror.ErrNotExist }
|
|||
func errClosed() error { return oserror.ErrClosed }
|
||||
func errNoDeadline() error { return poll.ErrNoDeadline }
|
||||
|
||||
// errDeadlineExceeded returns the value for os.ErrDeadlineExceeded.
|
||||
// This error comes from the internal/poll package, which is also
|
||||
// used by package net. Doing this this way ensures that the net
|
||||
// package will return os.ErrDeadlineExceeded for an exceeded deadline,
|
||||
// as documented by net.Conn.SetDeadline, without requiring any extra
|
||||
// work in the net package and without requiring the internal/poll
|
||||
// package to import os (which it can't, because that would be circular).
|
||||
func errDeadlineExceeded() error { return poll.ErrDeadlineExceeded }
|
||||
|
||||
type timeout interface {
|
||||
Timeout() bool
|
||||
}
|
||||
|
|
|
|||
|
|
@ -526,10 +526,12 @@ func (f *File) Chmod(mode FileMode) error { return f.chmod(mode) }
|
|||
// After a deadline has been exceeded, the connection can be refreshed
|
||||
// by setting a deadline in the future.
|
||||
//
|
||||
// An error returned after a timeout fails will implement the
|
||||
// Timeout method, and calling the Timeout method will return true.
|
||||
// The PathError and SyscallError types implement the Timeout method.
|
||||
// In general, call IsTimeout to test whether an error indicates a timeout.
|
||||
// If the deadline is exceeded a call to Read or Write or to other I/O
|
||||
// methods will return an error that wraps ErrDeadlineExceeded.
|
||||
// This can be tested using errors.Is(err, os.ErrDeadlineExceeded).
|
||||
// That error implements the Timeout method, and calling the Timeout
|
||||
// method will return true, but there are other possible errors for which
|
||||
// the Timeout will return true even if the deadline has not been exceeded.
|
||||
//
|
||||
// An idle timeout can be implemented by repeatedly extending
|
||||
// the deadline after successful Read or Write calls.
|
||||
|
|
|
|||
|
|
@ -2527,3 +2527,15 @@ func TestReaddirSmallSeek(t *testing.T) {
|
|||
t.Fatalf("first names: %v, second names: %v", names1, names2)
|
||||
}
|
||||
}
|
||||
|
||||
// isDeadlineExceeded reports whether err is or wraps os.ErrDeadlineExceeded.
|
||||
// We also check that the error has a Timeout method that returns true.
|
||||
func isDeadlineExceeded(err error) bool {
|
||||
if !IsTimeout(err) {
|
||||
return false
|
||||
}
|
||||
if !errors.Is(err, ErrDeadlineExceeded) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -275,7 +275,7 @@ func newFileTest(t *testing.T, blocking bool) {
|
|||
_, err := file.Read(b)
|
||||
if !blocking {
|
||||
// We want it to fail with a timeout.
|
||||
if !IsTimeout(err) {
|
||||
if !isDeadlineExceeded(err) {
|
||||
t.Fatalf("No timeout reading from file: %v", err)
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ package os_test
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"internal/poll"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
|
|
@ -57,9 +56,9 @@ var readTimeoutTests = []struct {
|
|||
}{
|
||||
// Tests that read deadlines work, even if there's data ready
|
||||
// to be read.
|
||||
{-5 * time.Second, [2]error{poll.ErrTimeout, poll.ErrTimeout}},
|
||||
{-5 * time.Second, [2]error{os.ErrDeadlineExceeded, os.ErrDeadlineExceeded}},
|
||||
|
||||
{50 * time.Millisecond, [2]error{nil, poll.ErrTimeout}},
|
||||
{50 * time.Millisecond, [2]error{nil, os.ErrDeadlineExceeded}},
|
||||
}
|
||||
|
||||
func TestReadTimeout(t *testing.T) {
|
||||
|
|
@ -85,7 +84,7 @@ func TestReadTimeout(t *testing.T) {
|
|||
for {
|
||||
n, err := r.Read(b[:])
|
||||
if xerr != nil {
|
||||
if !os.IsTimeout(err) {
|
||||
if !isDeadlineExceeded(err) {
|
||||
t.Fatalf("#%d/%d: %v", i, j, err)
|
||||
}
|
||||
}
|
||||
|
|
@ -148,9 +147,9 @@ var writeTimeoutTests = []struct {
|
|||
}{
|
||||
// Tests that write deadlines work, even if there's buffer
|
||||
// space available to write.
|
||||
{-5 * time.Second, [2]error{poll.ErrTimeout, poll.ErrTimeout}},
|
||||
{-5 * time.Second, [2]error{os.ErrDeadlineExceeded, os.ErrDeadlineExceeded}},
|
||||
|
||||
{10 * time.Millisecond, [2]error{nil, poll.ErrTimeout}},
|
||||
{10 * time.Millisecond, [2]error{nil, os.ErrDeadlineExceeded}},
|
||||
}
|
||||
|
||||
func TestWriteTimeout(t *testing.T) {
|
||||
|
|
@ -172,7 +171,7 @@ func TestWriteTimeout(t *testing.T) {
|
|||
for {
|
||||
n, err := w.Write([]byte("WRITE TIMEOUT TEST"))
|
||||
if xerr != nil {
|
||||
if !os.IsTimeout(err) {
|
||||
if !isDeadlineExceeded(err) {
|
||||
t.Fatalf("%d: %v", j, err)
|
||||
}
|
||||
}
|
||||
|
|
@ -246,7 +245,7 @@ func timeoutReader(r *os.File, d, min, max time.Duration, ch chan<- error) {
|
|||
var n int
|
||||
n, err = r.Read(b)
|
||||
t1 := time.Now()
|
||||
if n != 0 || err == nil || !os.IsTimeout(err) {
|
||||
if n != 0 || err == nil || !isDeadlineExceeded(err) {
|
||||
err = fmt.Errorf("Read did not return (0, timeout): (%d, %v)", n, err)
|
||||
return
|
||||
}
|
||||
|
|
@ -275,7 +274,7 @@ func TestReadTimeoutFluctuation(t *testing.T) {
|
|||
case <-max.C:
|
||||
t.Fatal("Read took over 1s; expected 0.1s")
|
||||
case err := <-ch:
|
||||
if !os.IsTimeout(err) {
|
||||
if !isDeadlineExceeded(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
|
@ -297,7 +296,7 @@ func timeoutWriter(w *os.File, d, min, max time.Duration, ch chan<- error) {
|
|||
}
|
||||
}
|
||||
t1 := time.Now()
|
||||
if err == nil || !os.IsTimeout(err) {
|
||||
if err == nil || !isDeadlineExceeded(err) {
|
||||
err = fmt.Errorf("Write did not return (any, timeout): (%d, %v)", n, err)
|
||||
return
|
||||
}
|
||||
|
|
@ -327,7 +326,7 @@ func TestWriteTimeoutFluctuation(t *testing.T) {
|
|||
case <-max.C:
|
||||
t.Fatalf("Write took over %v; expected 0.1s", d)
|
||||
case err := <-ch:
|
||||
if !os.IsTimeout(err) {
|
||||
if !isDeadlineExceeded(err) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
|
@ -438,7 +437,7 @@ func testVariousDeadlines(t *testing.T) {
|
|||
|
||||
select {
|
||||
case res := <-actvch:
|
||||
if os.IsTimeout(res.err) {
|
||||
if !isDeadlineExceeded(err) {
|
||||
t.Logf("good client timeout after %v, reading %d bytes", res.d, res.n)
|
||||
} else {
|
||||
t.Fatalf("client Copy = %d, %v; want timeout", res.n, res.err)
|
||||
|
|
@ -494,7 +493,7 @@ func TestReadWriteDeadlineRace(t *testing.T) {
|
|||
var b [1]byte
|
||||
for i := 0; i < N; i++ {
|
||||
_, err := r.Read(b[:])
|
||||
if err != nil && !os.IsTimeout(err) {
|
||||
if err != nil && !isDeadlineExceeded(err) {
|
||||
t.Error("Read returned non-timeout error", err)
|
||||
}
|
||||
}
|
||||
|
|
@ -504,7 +503,7 @@ func TestReadWriteDeadlineRace(t *testing.T) {
|
|||
var b [1]byte
|
||||
for i := 0; i < N; i++ {
|
||||
_, err := w.Write(b[:])
|
||||
if err != nil && !os.IsTimeout(err) {
|
||||
if err != nil && !isDeadlineExceeded(err) {
|
||||
t.Error("Write returned non-timeout error", err)
|
||||
}
|
||||
}
|
||||
|
|
@ -541,7 +540,7 @@ func TestRacyRead(t *testing.T) {
|
|||
_, err := r.Read(b1)
|
||||
copy(b1, b2) // Mutate b1 to trigger potential race
|
||||
if err != nil {
|
||||
if !os.IsTimeout(err) {
|
||||
if !isDeadlineExceeded(err) {
|
||||
t.Error(err)
|
||||
}
|
||||
r.SetReadDeadline(time.Now().Add(time.Millisecond))
|
||||
|
|
@ -580,7 +579,7 @@ func TestRacyWrite(t *testing.T) {
|
|||
_, err := w.Write(b1)
|
||||
copy(b1, b2) // Mutate b1 to trigger potential race
|
||||
if err != nil {
|
||||
if !os.IsTimeout(err) {
|
||||
if !isDeadlineExceeded(err) {
|
||||
t.Error(err)
|
||||
}
|
||||
w.SetWriteDeadline(time.Now().Add(time.Millisecond))
|
||||
|
|
|
|||
Loading…
Reference in New Issue