Prevent timeout in TestConcurrentExchanges (#862)

This eliminates the possibility of deadlocking the handler and also
simplifies the test considerably.
This commit is contained in:
Tom Thorogood 2018-12-04 17:59:08 +10:30 committed by Miek Gieben
parent 65b2aa0a63
commit 870a59089c
2 changed files with 21 additions and 27 deletions

View File

@ -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")

View File

@ -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
} }