]> Cypherpunks repositories - gostls13.git/commitdiff
syscall, net: make deadline changes affect blocked read/write calls on nacl
authorBrad Fitzpatrick <bradfitz@golang.org>
Sat, 15 Oct 2016 15:56:51 +0000 (16:56 +0100)
committerBrad Fitzpatrick <bradfitz@golang.org>
Mon, 17 Oct 2016 05:53:00 +0000 (05:53 +0000)
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>
src/net/net_test.go
src/syscall/net_nacl.go

index f58b93e74b13d9fa489161c44e5b2756f73f95ce..640bcac80861cd6188668ab73af846de374e74b5 100644 (file)
@@ -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)
+}
index 1a0122c4b68a5f84c5b512ec21f0d65de6a987f3..9dc5d0ca0b65b591a45ac63869124622b0499798 100644 (file)
@@ -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
 }