Skip to content

Commit

Permalink
Add a concurrent read/write test to plugin.EntryMap
Browse files Browse the repository at this point in the history
Signed-off-by: Enis Inan <[email protected]>
  • Loading branch information
ekinanp committed Oct 22, 2019
1 parent 97f24f2 commit a049a3e
Showing 1 changed file with 69 additions and 0 deletions.
69 changes: 69 additions & 0 deletions plugin/entryMap_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package plugin

import (
"strconv"
"sync"
"testing"
"time"

"github.com/stretchr/testify/suite"
)
Expand Down Expand Up @@ -67,6 +70,72 @@ func (suite *EntryMapTestSuite) TestRange() {
suite.True(count <= 1)
}

func (suite *EntryMapTestSuite) TestConcurrentReadWrite() {
m := newEntryMap()

var wg sync.WaitGroup
var startCh = make(chan struct{})
var doneCh = make(chan struct{})

// Load, Delete, and Range all acquire locks to avoid a concurrent
// read/write panic. Thus, this test launches goroutines that invoke
// each method concurrently. Idea is that if any one of those methods
// fail to acquire a lock (including the right lock, like a Write lock
// for Delete), then this test will panic. NumGoroutinesPerMethod is
// arbitrary, idea is it should be high enough that we can detect a panic
// but low enough that the test won't take too long.
//
// The test passes if all the goroutines successfully return.
//
// NOTE: Testing Len() is a bit tricky because a concurrent len(mp) and
// delete(mp) does not cause a read/write panic. Since its implementation is
// simple enough, we omit Len() to avoid further complicating the tests.
NumGoroutinesPerMethod := 40
for i := 0; i < NumGoroutinesPerMethod; i++ {
key := strconv.Itoa(i)
m.mp[key] = nil

// Load
wg.Add(1)
go func(i int) {
defer wg.Done()
<-startCh
m.Load(key)
}(i)

// Delete
wg.Add(1)
go func(i int) {
defer wg.Done()
<-startCh
m.Delete(key)
}(i)

// Range
wg.Add(1)
go func(i int) {
defer wg.Done()
<-startCh
m.Range(func(_ string, _ Entry) bool {
return false
})
}(i)
}
time.AfterFunc(5*time.Second, func() {
select {
case <-doneCh:
// Pass-thru
default:
panic("goroutines did not successfully return after 5 seconds. Did you forget to release a lock?")
}
})

// Start the goroutines and wait for them to finish.
close(startCh)
wg.Wait()
close(doneCh)
}

func TestEntryMap(t *testing.T) {
suite.Run(t, new(EntryMapTestSuite))
}

0 comments on commit a049a3e

Please sign in to comment.