package net
import (
+ "errors"
+ "fmt"
"io"
"net/internal/socktest"
"os"
}
}
}
+
+// 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)
+}
// The simulation is not particularly tied to NaCl,
// but other systems have real networks.
+// All int64 times are UnixNanos.
+
package syscall
import (
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() {
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
}
}
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 {
}
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 {
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
}
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
}