Skip to content

Commit

Permalink
net: change SetTimeout to SetDeadline
Browse files Browse the repository at this point in the history
Previously, a timeout (in int64 nanoseconds) applied to a granularity
even smaller than one operation:  a 100 byte read with a 1 second timeout
could take 100 seconds, if the bytes all arrived on the network 1 second
apart.  This was confusing.

Rather than making the timeout granularity be per-Read/Write,
this CL makes callers set an absolute deadline (in time.Time)
after which operations will fail.  This makes it possible to
set deadlines at higher levels, without knowing exactly how
many read/write operations will happen in e.g. reading an HTTP
request.

Fixes #2723

R=r, rsc, dave
CC=golang-dev
https://golang.org/cl/5555048
  • Loading branch information
bradfitz committed Jan 19, 2012
1 parent 5e77b00 commit b71883e
Show file tree
Hide file tree
Showing 23 changed files with 254 additions and 235 deletions.
15 changes: 13 additions & 2 deletions doc/go1.html
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,17 @@ <h3 id="http">The http package</h3>
uses of <code>RawURL</code>, which must be fixed by hand.
</p>

<h3 id="net">The net package</h3>

<p>In Go 1, the various <code>SetTimeout</code>,
<code>SetReadTimeout</code>, and <code>SetWriteTimeout</code> methods
have been replaced with <code>SetDeadline</code>,
<code>SetReadDeadline</code>, and <code>SetWriteDeadline</code>,
respectively. Rather than taking a timeout value in nanoseconds that
apply to any activity on the connection, the new methods set an
absolute deadline (as a <code>time.Time</code> value) after which
reads and writes will time out and no longer block.</p>

<h3 id="strconv">The strconv package</h3>

<p>
Expand Down Expand Up @@ -957,8 +968,8 @@ <h3 id="go">The package tree go</h3>
</pre>

<p>
where the new <code>mode</mode> parameter specifies the operation mode:
if set to <a href="go/doc/#AllDecls"><code>AllDecls</a>, all declarations
where the new <code>mode</code> parameter specifies the operation mode:
if set to <a href="go/doc/#AllDecls"><code>AllDecls</code></a>, all declarations
(not just exported ones) are considered.
The function <code>NewFileDoc</code> was removed, and the function
<code>CommentText</code> has become the method
Expand Down
15 changes: 13 additions & 2 deletions doc/go1.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,17 @@ Gofix will update the few programs that are affected except for
uses of <code>RawURL</code>, which must be fixed by hand.
</p>

<h3 id="net">The net package</h3>

<p>In Go 1, the various <code>SetTimeout</code>,
<code>SetReadTimeout</code>, and <code>SetWriteTimeout</code> methods
have been replaced with <code>SetDeadline</code>,
<code>SetReadDeadline</code>, and <code>SetWriteDeadline</code>,
respectively. Rather than taking a timeout value in nanoseconds that
apply to any activity on the connection, the new methods set an
absolute deadline (as a <code>time.Time</code> value) after which
reads and writes will time out and no longer block.</p>

<h3 id="strconv">The strconv package</h3>

<p>
Expand Down Expand Up @@ -861,8 +872,8 @@ documentation for a package is created with:
</pre>

<p>
where the new <code>mode</mode> parameter specifies the operation mode:
if set to <a href="go/doc/#AllDecls"><code>AllDecls</a>, all declarations
where the new <code>mode</code> parameter specifies the operation mode:
if set to <a href="go/doc/#AllDecls"><code>AllDecls</code></a>, all declarations
(not just exported ones) are considered.
The function <code>NewFileDoc</code> was removed, and the function
<code>CommentText</code> has become the method
Expand Down
26 changes: 13 additions & 13 deletions src/pkg/crypto/tls/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"io"
"net"
"sync"
"time"
)

// A Conn represents a secured connection.
Expand Down Expand Up @@ -86,24 +87,23 @@ func (c *Conn) RemoteAddr() net.Addr {
return c.conn.RemoteAddr()
}

// SetTimeout sets the read deadline associated with the connection.
// SetDeadline sets the read deadline associated with the connection.
// There is no write deadline.
func (c *Conn) SetTimeout(nsec int64) error {
return c.conn.SetTimeout(nsec)
// A zero value for t means Read will not time out.
func (c *Conn) SetDeadline(t time.Time) error {
return c.conn.SetDeadline(t)
}

// SetReadTimeout sets the time (in nanoseconds) that
// Read will wait for data before returning a net.Error
// with Timeout() == true.
// Setting nsec == 0 (the default) disables the deadline.
func (c *Conn) SetReadTimeout(nsec int64) error {
return c.conn.SetReadTimeout(nsec)
// SetReadDeadline sets the read deadline on the underlying connection.
// A zero value for t means Read will not time out.
func (c *Conn) SetReadDeadline(t time.Time) error {
return c.conn.SetReadDeadline(t)
}

// SetWriteTimeout exists to satisfy the net.Conn interface
// SetWriteDeadline exists to satisfy the net.Conn interface
// but is not implemented by TLS. It always returns an error.
func (c *Conn) SetWriteTimeout(nsec int64) error {
return errors.New("TLS does not support SetWriteTimeout")
func (c *Conn) SetWriteDeadline(t time.Time) error {
return errors.New("TLS does not support SetWriteDeadline")
}

// A halfConn represents one direction of the record layer
Expand Down Expand Up @@ -744,7 +744,7 @@ func (c *Conn) Write(b []byte) (n int, err error) {
}

// Read can be made to time out and return a net.Error with Timeout() == true
// after a fixed time limit; see SetTimeout and SetReadTimeout.
// after a fixed time limit; see SetDeadline and SetReadDeadline.
func (c *Conn) Read(b []byte) (n int, err error) {
if err = c.Handshake(); err != nil {
return
Expand Down
31 changes: 15 additions & 16 deletions src/pkg/exp/ssh/tcpip.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"fmt"
"io"
"net"
"time"
)

// Dial initiates a connection to the addr from the remote host.
Expand Down Expand Up @@ -107,27 +108,25 @@ func (t *tcpchanconn) RemoteAddr() net.Addr {
return t.raddr
}

// SetTimeout sets the read and write deadlines associated
// SetDeadline sets the read and write deadlines associated
// with the connection.
func (t *tcpchanconn) SetTimeout(nsec int64) error {
if err := t.SetReadTimeout(nsec); err != nil {
func (t *tcpchanconn) SetDeadline(deadline time.Time) error {
if err := t.SetReadDeadline(deadline); err != nil {
return err
}
return t.SetWriteTimeout(nsec)
return t.SetWriteDeadline(deadline)
}

// SetReadTimeout sets the time (in nanoseconds) that
// Read will wait for data before returning an error with Timeout() == true.
// Setting nsec == 0 (the default) disables the deadline.
func (t *tcpchanconn) SetReadTimeout(nsec int64) error {
return errors.New("ssh: tcpchan: timeout not supported")
// SetReadDeadline sets the read deadline.
// A zero value for t means Read will not time out.
// After the deadline, the error from Read will implement net.Error
// with Timeout() == true.
func (t *tcpchanconn) SetReadDeadline(deadline time.Time) error {
return errors.New("ssh: tcpchan: deadline not supported")
}

// SetWriteTimeout sets the time (in nanoseconds) that
// Write will wait to send its data before returning an error with Timeout() == true.
// Setting nsec == 0 (the default) disables the deadline.
// Even if write times out, it may return n > 0, indicating that
// some of the data was successfully written.
func (t *tcpchanconn) SetWriteTimeout(nsec int64) error {
return errors.New("ssh: tcpchan: timeout not supported")
// SetWriteDeadline exists to satisfy the net.Conn interface
// but is not implemented by this type. It always returns an error.
func (t *tcpchanconn) SetWriteDeadline(deadline time.Time) error {
return errors.New("ssh: tcpchan: deadline not supported")
}
3 changes: 2 additions & 1 deletion src/pkg/log/syslog/syslog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"log"
"net"
"testing"
"time"
)

var serverAddr string
Expand All @@ -31,7 +32,7 @@ func startServer(done chan<- string) {
log.Fatalf("net.ListenPacket failed udp :0 %v", e)
}
serverAddr = c.LocalAddr().String()
c.SetReadTimeout(100e6) // 100ms
c.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
go runSyslog(c, done)
}

Expand Down
6 changes: 5 additions & 1 deletion src/pkg/net/dnsclient_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ func exchange(cfg *dnsConfig, c Conn, name string, qtype uint16) (*dnsMsg, error
return nil, err
}

c.SetReadTimeout(int64(cfg.timeout) * 1e9) // nanoseconds
if cfg.timeout == 0 {
c.SetReadDeadline(time.Time{})
} else {
c.SetReadDeadline(time.Now().Add(time.Duration(cfg.timeout) * time.Second))
}

buf := make([]byte, 2000) // More than enough.
n, err = c.Read(buf)
Expand Down
45 changes: 4 additions & 41 deletions src/pkg/net/fd.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,10 @@ type netFD struct {
raddr Addr

// owned by client
rdeadline_delta int64
rdeadline int64
rio sync.Mutex
wdeadline_delta int64
wdeadline int64
wio sync.Mutex
rdeadline int64
rio sync.Mutex
wdeadline int64
wio sync.Mutex

// owned by fd wait server
ncr, ncw int
Expand Down Expand Up @@ -388,11 +386,6 @@ func (fd *netFD) Read(p []byte) (n int, err error) {
if fd.sysfile == nil {
return 0, os.EINVAL
}
if fd.rdeadline_delta > 0 {
fd.rdeadline = pollserver.Now() + fd.rdeadline_delta
} else {
fd.rdeadline = 0
}
for {
n, err = syscall.Read(fd.sysfile.Fd(), p)
if err == syscall.EAGAIN {
Expand Down Expand Up @@ -423,11 +416,6 @@ func (fd *netFD) ReadFrom(p []byte) (n int, sa syscall.Sockaddr, err error) {
defer fd.rio.Unlock()
fd.incref()
defer fd.decref()
if fd.rdeadline_delta > 0 {
fd.rdeadline = pollserver.Now() + fd.rdeadline_delta
} else {
fd.rdeadline = 0
}
for {
n, sa, err = syscall.Recvfrom(fd.sysfd, p, 0)
if err == syscall.EAGAIN {
Expand Down Expand Up @@ -456,11 +444,6 @@ func (fd *netFD) ReadMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.S
defer fd.rio.Unlock()
fd.incref()
defer fd.decref()
if fd.rdeadline_delta > 0 {
fd.rdeadline = pollserver.Now() + fd.rdeadline_delta
} else {
fd.rdeadline = 0
}
for {
n, oobn, flags, sa, err = syscall.Recvmsg(fd.sysfd, p, oob, 0)
if err == syscall.EAGAIN {
Expand Down Expand Up @@ -493,11 +476,6 @@ func (fd *netFD) Write(p []byte) (n int, err error) {
if fd.sysfile == nil {
return 0, os.EINVAL
}
if fd.wdeadline_delta > 0 {
fd.wdeadline = pollserver.Now() + fd.wdeadline_delta
} else {
fd.wdeadline = 0
}
nn := 0

for {
Expand Down Expand Up @@ -539,11 +517,6 @@ func (fd *netFD) WriteTo(p []byte, sa syscall.Sockaddr) (n int, err error) {
defer fd.wio.Unlock()
fd.incref()
defer fd.decref()
if fd.wdeadline_delta > 0 {
fd.wdeadline = pollserver.Now() + fd.wdeadline_delta
} else {
fd.wdeadline = 0
}
for {
err = syscall.Sendto(fd.sysfd, p, 0, sa)
if err == syscall.EAGAIN {
Expand Down Expand Up @@ -571,11 +544,6 @@ func (fd *netFD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oob
defer fd.wio.Unlock()
fd.incref()
defer fd.decref()
if fd.wdeadline_delta > 0 {
fd.wdeadline = pollserver.Now() + fd.wdeadline_delta
} else {
fd.wdeadline = 0
}
for {
err = syscall.Sendmsg(fd.sysfd, p, oob, sa, 0)
if err == syscall.EAGAIN {
Expand Down Expand Up @@ -603,11 +571,6 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err err

fd.incref()
defer fd.decref()
if fd.rdeadline_delta > 0 {
fd.rdeadline = pollserver.Now() + fd.rdeadline_delta
} else {
fd.rdeadline = 0
}

// See ../syscall/exec.go for description of ForkLock.
// It is okay to hold the lock across syscall.Accept
Expand Down
36 changes: 20 additions & 16 deletions src/pkg/net/fd_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,12 +150,13 @@ func (s *ioSrv) ProcessRemoteIO() {
}

// ExecIO executes a single io operation. It either executes it
// inline, or, if timeouts are employed, passes the request onto
// inline, or, if a deadline is employed, passes the request onto
// a special goroutine and waits for completion or cancels request.
func (s *ioSrv) ExecIO(oi anOpIface, deadline_delta int64) (n int, err error) {
// deadline is unix nanos.
func (s *ioSrv) ExecIO(oi anOpIface, deadline int64) (n int, err error) {
var e error
o := oi.Op()
if deadline_delta > 0 {
if deadline != 0 {
// Send request to a special dedicated thread,
// so it can stop the io with CancelIO later.
s.submchan <- oi
Expand All @@ -172,12 +173,17 @@ func (s *ioSrv) ExecIO(oi anOpIface, deadline_delta int64) (n int, err error) {
return 0, &OpError{oi.Name(), o.fd.net, o.fd.laddr, e}
}
// Wait for our request to complete.
// TODO(rsc): This should stop the timer.
var r ioResult
if deadline_delta > 0 {
if deadline != 0 {
dt := deadline - time.Now().UnixNano()
if dt < 1 {
dt = 1
}
ticker := time.NewTicker(time.Duration(dt) * time.Nanosecond)
defer ticker.Stop()
select {
case r = <-o.resultc:
case <-time.After(time.Duration(deadline_delta) * time.Nanosecond):
case <-ticker.C:
s.canchan <- oi
<-o.errnoc
r = <-o.resultc
Expand Down Expand Up @@ -232,12 +238,10 @@ type netFD struct {
errnoc [2]chan error // read/write submit or cancel operation errors

// owned by client
rdeadline_delta int64
rdeadline int64
rio sync.Mutex
wdeadline_delta int64
wdeadline int64
wio sync.Mutex
rdeadline int64
rio sync.Mutex
wdeadline int64
wio sync.Mutex
}

func allocFD(fd syscall.Handle, family, proto int, net string) (f *netFD) {
Expand Down Expand Up @@ -357,7 +361,7 @@ func (fd *netFD) Read(buf []byte) (n int, err error) {
}
var o readOp
o.Init(fd, buf, 'r')
n, err = iosrv.ExecIO(&o, fd.rdeadline_delta)
n, err = iosrv.ExecIO(&o, fd.rdeadline)
if err == nil && n == 0 {
err = io.EOF
}
Expand Down Expand Up @@ -398,7 +402,7 @@ func (fd *netFD) ReadFrom(buf []byte) (n int, sa syscall.Sockaddr, err error) {
var o readFromOp
o.Init(fd, buf, 'r')
o.rsan = int32(unsafe.Sizeof(o.rsa))
n, err = iosrv.ExecIO(&o, fd.rdeadline_delta)
n, err = iosrv.ExecIO(&o, fd.rdeadline)
if err != nil {
return 0, nil, err
}
Expand Down Expand Up @@ -434,7 +438,7 @@ func (fd *netFD) Write(buf []byte) (n int, err error) {
}
var o writeOp
o.Init(fd, buf, 'w')
return iosrv.ExecIO(&o, fd.wdeadline_delta)
return iosrv.ExecIO(&o, fd.wdeadline)
}

// WriteTo to network.
Expand Down Expand Up @@ -470,7 +474,7 @@ func (fd *netFD) WriteTo(buf []byte, sa syscall.Sockaddr) (n int, err error) {
var o writeToOp
o.Init(fd, buf, 'w')
o.sa = sa
return iosrv.ExecIO(&o, fd.wdeadline_delta)
return iosrv.ExecIO(&o, fd.wdeadline)
}

// Accept new network connections.
Expand Down

0 comments on commit b71883e

Please sign in to comment.