Skip to content

Commit 7a7ea01

Browse files
committed
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 <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent cd2c9df commit 7a7ea01

File tree

2 files changed

+89
-3
lines changed

2 files changed

+89
-3
lines changed

src/net/net_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
package net
66

77
import (
8+
"errors"
9+
"fmt"
810
"io"
911
"net/internal/socktest"
1012
"os"
@@ -449,3 +451,49 @@ func withTCPConnPair(t *testing.T, peer1, peer2 func(c *TCPConn) error) {
449451
}
450452
}
451453
}
454+
455+
// Tests that a blocked Read is interrupted by a concurrent SetReadDeadline
456+
// modifying that Conn's read deadline to the past.
457+
// See golang.org/cl/30164 which documented this. The net/http package
458+
// depends on this.
459+
func TestReadTimeoutUnblocksRead(t *testing.T) {
460+
serverDone := make(chan struct{})
461+
server := func(cs *TCPConn) error {
462+
defer close(serverDone)
463+
errc := make(chan error, 1)
464+
go func() {
465+
defer close(errc)
466+
go func() {
467+
// TODO: find a better way to wait
468+
// until we're blocked in the cs.Read
469+
// call below. Sleep is lame.
470+
time.Sleep(100 * time.Millisecond)
471+
472+
// Interrupt the upcoming Read, unblocking it:
473+
cs.SetReadDeadline(time.Unix(123, 0)) // time in the past
474+
}()
475+
var buf [1]byte
476+
n, err := cs.Read(buf[:1])
477+
if n != 0 || err == nil {
478+
errc <- fmt.Errorf("Read = %v, %v; want 0, non-nil", n, err)
479+
}
480+
}()
481+
select {
482+
case err := <-errc:
483+
return err
484+
case <-time.After(5 * time.Second):
485+
buf := make([]byte, 2<<20)
486+
buf = buf[:runtime.Stack(buf, true)]
487+
println("Stacks at timeout:\n", string(buf))
488+
return errors.New("timeout waiting for Read to finish")
489+
}
490+
491+
}
492+
// Do nothing in the client. Never write. Just wait for the
493+
// server's half to be done.
494+
client := func(*TCPConn) error {
495+
<-serverDone
496+
return nil
497+
}
498+
withTCPConnPair(t, client, server)
499+
}

src/syscall/net_nacl.go

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
// The simulation is not particularly tied to NaCl,
77
// but other systems have real networks.
88

9+
// All int64 times are UnixNanos.
10+
911
package syscall
1012

1113
import (
@@ -50,6 +52,22 @@ func (t *timer) stop() {
5052
stopTimer(&t.r)
5153
}
5254

55+
func (t *timer) reset(q *queue, deadline int64) {
56+
if t.r.f != nil {
57+
t.stop()
58+
}
59+
if deadline == 0 {
60+
return
61+
}
62+
if t.r.f == nil {
63+
t.q = q
64+
t.r.f = timerExpired
65+
t.r.arg = t
66+
}
67+
t.r.when = deadline
68+
startTimer(&t.r)
69+
}
70+
5371
func timerExpired(i interface{}, seq uintptr) {
5472
t := i.(*timer)
5573
go func() {
@@ -233,9 +251,11 @@ type queue struct {
233251
sync.Mutex
234252
canRead sync.Cond
235253
canWrite sync.Cond
236-
r int // total read index
237-
w int // total write index
238-
m int // index mask
254+
rtimer *timer // non-nil if in read
255+
wtimer *timer // non-nil if in write
256+
r int // total read index
257+
w int // total write index
258+
m int // index mask
239259
closed bool
240260
}
241261

@@ -259,9 +279,11 @@ func (q *queue) waitRead(n int, deadline int64) (int, error) {
259279
}
260280
var t timer
261281
t.start(q, deadline)
282+
q.rtimer = &t
262283
for q.w-q.r == 0 && !q.closed && !t.expired {
263284
q.canRead.Wait()
264285
}
286+
q.rtimer = nil
265287
t.stop()
266288
m := q.w - q.r
267289
if m == 0 && t.expired {
@@ -281,9 +303,11 @@ func (q *queue) waitWrite(n int, deadline int64) (int, error) {
281303
}
282304
var t timer
283305
t.start(q, deadline)
306+
q.wtimer = &t
284307
for q.w-q.r > q.m && !q.closed && !t.expired {
285308
q.canWrite.Wait()
286309
}
310+
q.wtimer = nil
287311
t.stop()
288312
m := q.m + 1 - (q.w - q.r)
289313
if m == 0 && t.expired {
@@ -871,6 +895,13 @@ func SetReadDeadline(fd int, t int64) error {
871895
return err
872896
}
873897
atomic.StoreInt64(&f.rddeadline, t)
898+
if bq := f.rd; bq != nil {
899+
bq.Lock()
900+
if timer := bq.rtimer; timer != nil {
901+
timer.reset(&bq.queue, t)
902+
}
903+
bq.Unlock()
904+
}
874905
return nil
875906
}
876907

@@ -884,6 +915,13 @@ func SetWriteDeadline(fd int, t int64) error {
884915
return err
885916
}
886917
atomic.StoreInt64(&f.wrdeadline, t)
918+
if bq := f.wr; bq != nil {
919+
bq.Lock()
920+
if timer := bq.wtimer; timer != nil {
921+
timer.reset(&bq.queue, t)
922+
}
923+
bq.Unlock()
924+
}
887925
return nil
888926
}
889927

0 commit comments

Comments
 (0)