|
|
|
|
@ -9,7 +9,6 @@ import ( |
|
|
|
|
"context" |
|
|
|
|
crand "crypto/rand" |
|
|
|
|
"errors" |
|
|
|
|
"expvar" |
|
|
|
|
"fmt" |
|
|
|
|
"io" |
|
|
|
|
"net" |
|
|
|
|
@ -215,10 +214,27 @@ func TestSendFreeze(t *testing.T) { |
|
|
|
|
cathyKey := newPrivateKey(t) |
|
|
|
|
cathyClient, cathyConn := newClient("cathy", cathyKey) |
|
|
|
|
|
|
|
|
|
var aliceCount, bobCount, cathyCount expvar.Int |
|
|
|
|
var ( |
|
|
|
|
aliceCh = make(chan struct{}, 32) |
|
|
|
|
bobCh = make(chan struct{}, 32) |
|
|
|
|
cathyCh = make(chan struct{}, 32) |
|
|
|
|
) |
|
|
|
|
chs := func(name string) chan struct{} { |
|
|
|
|
switch name { |
|
|
|
|
case "alice": |
|
|
|
|
return aliceCh |
|
|
|
|
case "bob": |
|
|
|
|
return bobCh |
|
|
|
|
case "cathy": |
|
|
|
|
return cathyCh |
|
|
|
|
default: |
|
|
|
|
panic("unknown ch: " + name) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
errCh := make(chan error, 4) |
|
|
|
|
recvAndCount := func(count *expvar.Int, name string, client *Client) { |
|
|
|
|
recv := func(name string, client *Client) { |
|
|
|
|
ch := chs(name) |
|
|
|
|
for { |
|
|
|
|
b := make([]byte, 1<<9) |
|
|
|
|
m, err := client.Recv(b) |
|
|
|
|
@ -235,13 +251,16 @@ func TestSendFreeze(t *testing.T) { |
|
|
|
|
errCh <- fmt.Errorf("%s: zero Source address in ReceivedPacket", name) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
count.Add(1) |
|
|
|
|
select { |
|
|
|
|
case ch <- struct{}{}: |
|
|
|
|
default: |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
go recvAndCount(&aliceCount, "alice", aliceClient) |
|
|
|
|
go recvAndCount(&bobCount, "bob", bobClient) |
|
|
|
|
go recvAndCount(&cathyCount, "cathy", cathyClient) |
|
|
|
|
go recv("alice", aliceClient) |
|
|
|
|
go recv("bob", bobClient) |
|
|
|
|
go recv("cathy", cathyClient) |
|
|
|
|
|
|
|
|
|
var cancel func() |
|
|
|
|
go func() { |
|
|
|
|
@ -270,38 +289,52 @@ func TestSendFreeze(t *testing.T) { |
|
|
|
|
} |
|
|
|
|
}() |
|
|
|
|
|
|
|
|
|
var countSnapshot [3]int64 |
|
|
|
|
loadCounts := func() (adiff, bdiff, cdiff int64) { |
|
|
|
|
drainAny := func(ch chan struct{}) { |
|
|
|
|
// We are draining potentially infinite sources,
|
|
|
|
|
// so place some reasonable upper limit.
|
|
|
|
|
//
|
|
|
|
|
// The important thing here is to make sure that
|
|
|
|
|
// if any tokens remain in the channel, they
|
|
|
|
|
// must have been generated after drainAny was
|
|
|
|
|
// called.
|
|
|
|
|
for i := 0; i < cap(ch); i++ { |
|
|
|
|
select { |
|
|
|
|
case <-ch: |
|
|
|
|
default: |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
drain := func(t *testing.T, name string) bool { |
|
|
|
|
t.Helper() |
|
|
|
|
timer := time.NewTimer(1 * time.Second) |
|
|
|
|
defer timer.Stop() |
|
|
|
|
|
|
|
|
|
atotal := aliceCount.Value() |
|
|
|
|
btotal := bobCount.Value() |
|
|
|
|
ctotal := cathyCount.Value() |
|
|
|
|
|
|
|
|
|
adiff = atotal - countSnapshot[0] |
|
|
|
|
bdiff = btotal - countSnapshot[1] |
|
|
|
|
cdiff = ctotal - countSnapshot[2] |
|
|
|
|
|
|
|
|
|
countSnapshot[0] = atotal |
|
|
|
|
countSnapshot[1] = btotal |
|
|
|
|
countSnapshot[2] = ctotal |
|
|
|
|
|
|
|
|
|
t.Logf("count diffs: alice=%d, bob=%d, cathy=%d", adiff, bdiff, cdiff) |
|
|
|
|
return adiff, bdiff, cdiff |
|
|
|
|
// Ensure ch has at least one element.
|
|
|
|
|
ch := chs(name) |
|
|
|
|
select { |
|
|
|
|
case <-ch: |
|
|
|
|
case <-timer.C: |
|
|
|
|
t.Errorf("no packet received by %s", name) |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
// Drain remaining.
|
|
|
|
|
drainAny(ch) |
|
|
|
|
return true |
|
|
|
|
} |
|
|
|
|
isEmpty := func(t *testing.T, name string) { |
|
|
|
|
t.Helper() |
|
|
|
|
select { |
|
|
|
|
case <-chs(name): |
|
|
|
|
t.Errorf("packet received by %s, want none", name) |
|
|
|
|
default: |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
t.Run("initial send", func(t *testing.T) { |
|
|
|
|
time.Sleep(10 * time.Millisecond) |
|
|
|
|
a, b, c := loadCounts() |
|
|
|
|
if a != 0 { |
|
|
|
|
t.Errorf("alice diff=%d, want 0", a) |
|
|
|
|
} |
|
|
|
|
if b == 0 { |
|
|
|
|
t.Errorf("no bob diff, want positive value") |
|
|
|
|
} |
|
|
|
|
if c == 0 { |
|
|
|
|
t.Errorf("no cathy diff, want positive value") |
|
|
|
|
} |
|
|
|
|
drain(t, "bob") |
|
|
|
|
drain(t, "cathy") |
|
|
|
|
isEmpty(t, "alice") |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
t.Run("block cathy", func(t *testing.T) { |
|
|
|
|
@ -310,17 +343,12 @@ func TestSendFreeze(t *testing.T) { |
|
|
|
|
cathyConn.SetReadBlock(true) |
|
|
|
|
time.Sleep(2 * s.WriteTimeout) |
|
|
|
|
|
|
|
|
|
a, b, _ := loadCounts() |
|
|
|
|
if a != 0 { |
|
|
|
|
t.Errorf("alice diff=%d, want 0", a) |
|
|
|
|
} |
|
|
|
|
if b == 0 { |
|
|
|
|
t.Errorf("no bob diff, want positive value") |
|
|
|
|
} |
|
|
|
|
drain(t, "bob") |
|
|
|
|
drainAny(chs("cathy")) |
|
|
|
|
isEmpty(t, "alice") |
|
|
|
|
|
|
|
|
|
// Now wait a little longer, and ensure packets still flow to bob
|
|
|
|
|
time.Sleep(10 * time.Millisecond) |
|
|
|
|
if _, b, _ := loadCounts(); b == 0 { |
|
|
|
|
if !drain(t, "bob") { |
|
|
|
|
t.Errorf("connection alice->bob frozen by alice->cathy") |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
|