Commit 7a7ea01c authored by Brad Fitzpatrick's avatar Brad Fitzpatrick

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: 's avatarIan Lance Taylor <iant@golang.org>
parent cd2c9df7
......@@ -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
}
......
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