mirror of https://github.com/golang/go.git
syscall, net: make deadline changes affect blocked read/write calls on nacl
Flesh out nacl's fake network system to match how all the other platforms work: all other systems' SetReadDeadline and SetWriteDeadline affect currently-blocked read & write calls. This was documented in golang.org/cl/30164 because it was the status quo and existing packages relied on it. (notably the net/http package) And add a test. Change-Id: I074a1054dcabcedc97b173dad5e827f8babf7cfc Reviewed-on: https://go-review.googlesource.com/31178 Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
parent
cd2c9df761
commit
7a7ea01c65
|
|
@ -5,6 +5,8 @@
|
|||
package net
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/internal/socktest"
|
||||
"os"
|
||||
|
|
@ -449,3 +451,49 @@ func withTCPConnPair(t *testing.T, peer1, peer2 func(c *TCPConn) error) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that a blocked Read is interrupted by a concurrent SetReadDeadline
|
||||
// modifying that Conn's read deadline to the past.
|
||||
// See golang.org/cl/30164 which documented this. The net/http package
|
||||
// depends on this.
|
||||
func TestReadTimeoutUnblocksRead(t *testing.T) {
|
||||
serverDone := make(chan struct{})
|
||||
server := func(cs *TCPConn) error {
|
||||
defer close(serverDone)
|
||||
errc := make(chan error, 1)
|
||||
go func() {
|
||||
defer close(errc)
|
||||
go func() {
|
||||
// TODO: find a better way to wait
|
||||
// until we're blocked in the cs.Read
|
||||
// call below. Sleep is lame.
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
// Interrupt the upcoming Read, unblocking it:
|
||||
cs.SetReadDeadline(time.Unix(123, 0)) // time in the past
|
||||
}()
|
||||
var buf [1]byte
|
||||
n, err := cs.Read(buf[:1])
|
||||
if n != 0 || err == nil {
|
||||
errc <- fmt.Errorf("Read = %v, %v; want 0, non-nil", n, err)
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case err := <-errc:
|
||||
return err
|
||||
case <-time.After(5 * time.Second):
|
||||
buf := make([]byte, 2<<20)
|
||||
buf = buf[:runtime.Stack(buf, true)]
|
||||
println("Stacks at timeout:\n", string(buf))
|
||||
return errors.New("timeout waiting for Read to finish")
|
||||
}
|
||||
|
||||
}
|
||||
// Do nothing in the client. Never write. Just wait for the
|
||||
// server's half to be done.
|
||||
client := func(*TCPConn) error {
|
||||
<-serverDone
|
||||
return nil
|
||||
}
|
||||
withTCPConnPair(t, client, server)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
// The simulation is not particularly tied to NaCl,
|
||||
// but other systems have real networks.
|
||||
|
||||
// All int64 times are UnixNanos.
|
||||
|
||||
package syscall
|
||||
|
||||
import (
|
||||
|
|
@ -50,6 +52,22 @@ func (t *timer) stop() {
|
|||
stopTimer(&t.r)
|
||||
}
|
||||
|
||||
func (t *timer) reset(q *queue, deadline int64) {
|
||||
if t.r.f != nil {
|
||||
t.stop()
|
||||
}
|
||||
if deadline == 0 {
|
||||
return
|
||||
}
|
||||
if t.r.f == nil {
|
||||
t.q = q
|
||||
t.r.f = timerExpired
|
||||
t.r.arg = t
|
||||
}
|
||||
t.r.when = deadline
|
||||
startTimer(&t.r)
|
||||
}
|
||||
|
||||
func timerExpired(i interface{}, seq uintptr) {
|
||||
t := i.(*timer)
|
||||
go func() {
|
||||
|
|
@ -233,9 +251,11 @@ type queue struct {
|
|||
sync.Mutex
|
||||
canRead sync.Cond
|
||||
canWrite sync.Cond
|
||||
r int // total read index
|
||||
w int // total write index
|
||||
m int // index mask
|
||||
rtimer *timer // non-nil if in read
|
||||
wtimer *timer // non-nil if in write
|
||||
r int // total read index
|
||||
w int // total write index
|
||||
m int // index mask
|
||||
closed bool
|
||||
}
|
||||
|
||||
|
|
@ -259,9 +279,11 @@ func (q *queue) waitRead(n int, deadline int64) (int, error) {
|
|||
}
|
||||
var t timer
|
||||
t.start(q, deadline)
|
||||
q.rtimer = &t
|
||||
for q.w-q.r == 0 && !q.closed && !t.expired {
|
||||
q.canRead.Wait()
|
||||
}
|
||||
q.rtimer = nil
|
||||
t.stop()
|
||||
m := q.w - q.r
|
||||
if m == 0 && t.expired {
|
||||
|
|
@ -281,9 +303,11 @@ func (q *queue) waitWrite(n int, deadline int64) (int, error) {
|
|||
}
|
||||
var t timer
|
||||
t.start(q, deadline)
|
||||
q.wtimer = &t
|
||||
for q.w-q.r > q.m && !q.closed && !t.expired {
|
||||
q.canWrite.Wait()
|
||||
}
|
||||
q.wtimer = nil
|
||||
t.stop()
|
||||
m := q.m + 1 - (q.w - q.r)
|
||||
if m == 0 && t.expired {
|
||||
|
|
@ -871,6 +895,13 @@ func SetReadDeadline(fd int, t int64) error {
|
|||
return err
|
||||
}
|
||||
atomic.StoreInt64(&f.rddeadline, t)
|
||||
if bq := f.rd; bq != nil {
|
||||
bq.Lock()
|
||||
if timer := bq.rtimer; timer != nil {
|
||||
timer.reset(&bq.queue, t)
|
||||
}
|
||||
bq.Unlock()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -884,6 +915,13 @@ func SetWriteDeadline(fd int, t int64) error {
|
|||
return err
|
||||
}
|
||||
atomic.StoreInt64(&f.wrdeadline, t)
|
||||
if bq := f.wr; bq != nil {
|
||||
bq.Lock()
|
||||
if timer := bq.wtimer; timer != nil {
|
||||
timer.reset(&bq.queue, t)
|
||||
}
|
||||
bq.Unlock()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue