go: proposal: io: new Timeout interface implemented by errors
background
Currently there is a private interface in net, errors, and os that is used to check for if an error is a timeout:
type timeout interface {
Timeout() bool
}
While these interfaces can only used inside the packages they are defined in, the specification leaks into many public apis for other packages: net/http, syscall, context, etc.
implementation
We should expose one common interface:
package io
type Timeout interface {
Timeout() bool
}
alternatives
It would likely be better to expose io.ErrTimeout and ensure everybody implements errors.Is(err, io.ErrTimeout), but that would be a breaking change, so it is not advocated for here. That being said, I am in favour of that too, but for compatibility, it would need to be both. I also recall their being a larger discussion, that I cannot find, about the topic of ErrTemporary, ErrTimeout, and the like.
If we wanted to reserve io.Timeout for something better, we could call this io.Timeouter, but it sounds strange. io may not be the right package either, but os woudl make for a circular dependency and errors did did not seem, strictly speaking, better.
drawbacks
This interface is not strictly speaking required, but it is a compile time contract between library and consumer, and does make the following code read better:
var t interface{ Timeout() bool }
if errors.As(err, t); t.Timeout() { /* ... */ }
// versus
var t io.Timeout
if errors.As(err, t); t.Timeout() { /* ... */ }
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Comments: 15 (9 by maintainers)
ErrTimeout is a tricky one: see #33411 and the things it links to for reference.
As for a Timeout interface in package io, I’m not sure. It seems to me like timeouts are not strictly related to I/O.
I missed this before, but there is an extant
testing/iotest.ErrTimeout. Seems limited in scope, but it did not appear to be part of any of these discussions.If this is the direction we are pushing in, it strikes me that three classes of usage should be amended:
methods
Public symbols should be deprecated to clarify usage to consumers:
net.AddrError.Timeoutnet.DNSConfigError.Timeoutnet.DNSError.Timeoutnet.InvalidAddrError.Timeoutnet.OpError.Timeoutnet.UnknownNetworkError.Timeoutnet/url.Error.Timeoutos.IsTimeoutos.PathError.Timeoutos.SyscallError.Timeoutsyscall.Errno.Timeout(unix, windows, js)syscall.ErrorString.Timeout(plan9)Probably good to deprecate internal symbols too:
context.deadlineExceededError.Timeoutcrypto/tls.timeoutError.Timeoutinternal/poll.TimeoutError.Timeoutnet.addrinfoErrno.Timeoutnet.addrinfoErrno.Timeoutnet.addrionfo.Errno.Timeoutnet/http.http2httpError.Timeoutnet/http.httpError.Timeoutnet/http.tlsHandshakeTimeoutError.Timeoutnet.timeoutError.Timeoutgodoc
As already started by @ianlancetaylor, we should purge all mention from documentation, leaving the behaviour as vestigial. His CL 239705 covers:
net.PacketConn.ReadFromnet.PacketConn.WriteTonet.Conn.Readnet.Conn.WriteBut not:
os.File.SetDeadlinecrypto/tls.Conn.Readnet/http.Client.Donet/http.Client.Getnet/http.Getcallsite
While potentially overkill, it may be good to add a clarifying warning comment around current usage. It would serve to freeze the current implementation, while dissuading readers from copying the pattern.
context/context_test.go:650context/net_test.go:18context/net_test.go:19crypto/tls/handshake_client_test.go:1980crypto/tls/handshake_client_test.go:494crypto/tls/handshake_server_test.go:1591crypto/tls/tls_test.go:206net/dnsclient_unix.go:263net/error_test.go:654net/http/client_test.go:1274net/http/client_test.go:1319net/http/client_test.go:1985net/http/client_test.go:1986net/http/server.go:1755net/http/server.go:705net/http/transport_test.go:2275net/http/transport_test.go:3152net/lookup_test.go:559net/lookup_test.go:565net/mockserver_test.go:297net/mockserver_test.go:323net/mockserver_test.go:515net/net.go:502net/net.go:505net/protoconn_test.go:225net/protoconn_test.go:232net/protoconn_test.go:44net/protoconn_test.go:51net/rawconn_test.go:133net/rawconn_test.go:142net/rawconn_test.go:156net/rawconn_test.go:170net/timeout_test.go:197net/timeout_test.go:253net/timeout_test.go:347net/timeout_test.go:414net/timeout_test.go:471net/timeout_test.go:479net/timeout_test.go:525net/timeout_test.go:596net/timeout_test.go:644net/timeout_test.go:688net/timeout_test.go:721net/timeout_test.go:763net/timeout_test.go:88net/timeout_test.go:884net/udpsock_test.go:394net/udpsock_test.go:438net/unixsock_test.go:116net/unixsock_test.go:166net/url/url.go:35net/url/url_test.go:1622net/url/url_test.go:1623os/error.go:107os/error.go:53os/error.go:69syscall/syscall_js.go:78syscall/syscall_plan9.go:72syscall/syscall_unix.go:141syscall/syscall_windows.go:158vendor/golang.org/x/net/nettest/conntest.go:400vendor/golang.org/x/net/nettest/conntest.go:401Now that @neild mentions it, I agree: we shouldn’t be pushing the
Timeoutmethod or the hypotheticalos.ErrTimeoutat all. It’s been historically painful, and we should just let it drop.I sent the documentation-only https://golang.org/cl/239705 to help with that process.
If we decide that grouping together timeouts from different sources was a bad idea (which I don’t have a strong opinion on at the moment), then we should probably remove documentation that mentions the
Timeoutmethod and add deprecation notices tonet.Errorand any exported instances ofTimeout.