Prevent timeout in TestConcurrentExchanges (#862)
This eliminates the possibility of deadlocking the handler and also simplifies the test considerably.
This commit is contained in:
parent
65b2aa0a63
commit
870a59089c
|
@ -7,7 +7,6 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -521,17 +520,13 @@ func TestConcurrentExchanges(t *testing.T) {
|
||||||
cases[0] = new(Msg)
|
cases[0] = new(Msg)
|
||||||
cases[1] = new(Msg)
|
cases[1] = new(Msg)
|
||||||
cases[1].Truncated = true
|
cases[1].Truncated = true
|
||||||
for _, m := range cases {
|
|
||||||
block := make(chan struct{})
|
|
||||||
waiting := make(chan struct{})
|
|
||||||
|
|
||||||
|
for _, m := range cases {
|
||||||
mm := m // redeclare m so as not to trip the race detector
|
mm := m // redeclare m so as not to trip the race detector
|
||||||
handler := func(w ResponseWriter, req *Msg) {
|
handler := func(w ResponseWriter, req *Msg) {
|
||||||
r := mm.Copy()
|
r := mm.Copy()
|
||||||
r.SetReply(req)
|
r.SetReply(req)
|
||||||
|
|
||||||
waiting <- struct{}{}
|
|
||||||
<-block
|
|
||||||
w.WriteMsg(r)
|
w.WriteMsg(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -546,29 +541,24 @@ func TestConcurrentExchanges(t *testing.T) {
|
||||||
|
|
||||||
m := new(Msg)
|
m := new(Msg)
|
||||||
m.SetQuestion("miek.nl.", TypeSRV)
|
m.SetQuestion("miek.nl.", TypeSRV)
|
||||||
|
|
||||||
c := &Client{
|
c := &Client{
|
||||||
SingleInflight: true,
|
SingleInflight: true,
|
||||||
}
|
}
|
||||||
r := make([]*Msg, 2)
|
// Force this client to always return the same request,
|
||||||
|
// even though we're querying sequentially. Running the
|
||||||
|
// Exchange calls below concurrently can fail due to
|
||||||
|
// goroutine scheduling, but this simulates the same
|
||||||
|
// outcome.
|
||||||
|
c.group.dontDeleteForTesting = true
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
r := make([]*Msg, 2)
|
||||||
wg.Add(len(r))
|
for i := range r {
|
||||||
for i := 0; i < len(r); i++ {
|
r[i], _, _ = c.Exchange(m.Copy(), addrstr)
|
||||||
go func(i int) {
|
if r[i] == nil {
|
||||||
defer wg.Done()
|
t.Errorf("response %d is nil", i)
|
||||||
r[i], _, _ = c.Exchange(m.Copy(), addrstr)
|
}
|
||||||
if r[i] == nil {
|
|
||||||
t.Errorf("response %d is nil", i)
|
|
||||||
}
|
|
||||||
}(i)
|
|
||||||
}
|
}
|
||||||
select {
|
|
||||||
case <-waiting:
|
|
||||||
case <-time.After(time.Second):
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
close(block)
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
if r[0] == r[1] {
|
if r[0] == r[1] {
|
||||||
t.Errorf("got same response, expected non-shared responses")
|
t.Errorf("got same response, expected non-shared responses")
|
||||||
|
|
|
@ -23,6 +23,8 @@ type call struct {
|
||||||
type singleflight struct {
|
type singleflight struct {
|
||||||
sync.Mutex // protects m
|
sync.Mutex // protects m
|
||||||
m map[string]*call // lazily initialized
|
m map[string]*call // lazily initialized
|
||||||
|
|
||||||
|
dontDeleteForTesting bool // this is only to be used by TestConcurrentExchanges
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do executes and returns the results of the given function, making
|
// Do executes and returns the results of the given function, making
|
||||||
|
@ -49,9 +51,11 @@ func (g *singleflight) Do(key string, fn func() (*Msg, time.Duration, error)) (v
|
||||||
c.val, c.rtt, c.err = fn()
|
c.val, c.rtt, c.err = fn()
|
||||||
c.wg.Done()
|
c.wg.Done()
|
||||||
|
|
||||||
g.Lock()
|
if !g.dontDeleteForTesting {
|
||||||
delete(g.m, key)
|
g.Lock()
|
||||||
g.Unlock()
|
delete(g.m, key)
|
||||||
|
g.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
return c.val, c.rtt, c.err, c.dups > 0
|
return c.val, c.rtt, c.err, c.dups > 0
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue