Skip to content

Commit 86ddc85

Browse files
author
Bryan C. Mills
committed
syncmap: make quick-check output more readable
Add a test for Range with concurrent Loads and Stores. The previous quick-check tests generated long, mostly-non-ASCII strings that were hard to debug on failure; this change makes the keys and values short and human-readable, which also tends to produce more key collisions in the test as a side-effect. updates golang/go#18177 Change-Id: Ie56a64ec9fe295435682b90c3e6466ed5b349bf9 Reviewed-on: https://go-review.googlesource.com/37150 Reviewed-by: Russ Cox <[email protected]>
1 parent 37569ff commit 86ddc85

File tree

1 file changed

+102
-46
lines changed

1 file changed

+102
-46
lines changed

syncmap/map_test.go

+102-46
Original file line numberDiff line numberDiff line change
@@ -5,75 +5,70 @@
55
package syncmap_test
66

77
import (
8-
"fmt"
98
"math/rand"
109
"reflect"
10+
"runtime"
11+
"sync"
1112
"testing"
1213
"testing/quick"
1314

1415
"golang.org/x/sync/syncmap"
1516
)
1617

18+
type mapOp string
19+
20+
const (
21+
opLoad = mapOp("Load")
22+
opStore = mapOp("Store")
23+
opLoadOrStore = mapOp("LoadOrStore")
24+
opDelete = mapOp("Delete")
25+
)
26+
27+
var mapOps = [...]mapOp{opLoad, opStore, opLoadOrStore, opDelete}
28+
1729
// mapCall is a quick.Generator for calls on mapInterface.
1830
type mapCall struct {
19-
key interface{}
20-
apply func(mapInterface) (interface{}, bool)
21-
desc string
31+
op mapOp
32+
k, v interface{}
33+
}
34+
35+
func (c mapCall) apply(m mapInterface) (interface{}, bool) {
36+
switch c.op {
37+
case opLoad:
38+
return m.Load(c.k)
39+
case opStore:
40+
m.Store(c.k, c.v)
41+
return nil, false
42+
case opLoadOrStore:
43+
return m.LoadOrStore(c.k, c.v)
44+
case opDelete:
45+
m.Delete(c.k)
46+
return nil, false
47+
default:
48+
panic("invalid mapOp")
49+
}
2250
}
2351

2452
type mapResult struct {
2553
value interface{}
2654
ok bool
2755
}
2856

29-
var stringType = reflect.TypeOf("")
30-
3157
func randValue(r *rand.Rand) interface{} {
32-
k, ok := quick.Value(stringType, r)
33-
if !ok {
34-
panic(fmt.Sprintf("quick.Value(%v, _) failed", stringType))
58+
b := make([]byte, r.Intn(4))
59+
for i := range b {
60+
b[i] = 'a' + byte(rand.Intn(26))
3561
}
36-
return k.Interface()
62+
return string(b)
3763
}
3864

3965
func (mapCall) Generate(r *rand.Rand, size int) reflect.Value {
40-
k := randValue(r)
41-
42-
var (
43-
app func(mapInterface) (interface{}, bool)
44-
desc string
45-
)
46-
switch rand.Intn(4) {
47-
case 0:
48-
app = func(m mapInterface) (interface{}, bool) {
49-
return m.Load(k)
50-
}
51-
desc = fmt.Sprintf("Load(%q)", k)
52-
53-
case 1:
54-
v := randValue(r)
55-
app = func(m mapInterface) (interface{}, bool) {
56-
m.Store(k, v)
57-
return nil, false
58-
}
59-
desc = fmt.Sprintf("Store(%q, %q)", k, v)
60-
61-
case 2:
62-
v := randValue(r)
63-
app = func(m mapInterface) (interface{}, bool) {
64-
return m.LoadOrStore(k, v)
65-
}
66-
desc = fmt.Sprintf("LoadOrStore(%q, %q)", k, v)
67-
68-
case 3:
69-
app = func(m mapInterface) (interface{}, bool) {
70-
m.Delete(k)
71-
return nil, false
72-
}
73-
desc = fmt.Sprintf("Delete(%q)", k)
66+
c := mapCall{op: mapOps[rand.Intn(len(mapOps))], k: randValue(r)}
67+
switch c.op {
68+
case opStore, opLoadOrStore:
69+
c.v = randValue(r)
7470
}
75-
76-
return reflect.ValueOf(mapCall{k, app, desc})
71+
return reflect.ValueOf(c)
7772
}
7873

7974
func applyCalls(m mapInterface, calls []mapCall) (results []mapResult, final map[interface{}]interface{}) {
@@ -114,3 +109,64 @@ func TestMapMatchesDeepCopy(t *testing.T) {
114109
t.Error(err)
115110
}
116111
}
112+
113+
func TestConcurrentRange(t *testing.T) {
114+
const mapSize = 1 << 10
115+
116+
m := new(syncmap.Map)
117+
for n := int64(1); n <= mapSize; n++ {
118+
m.Store(n, int64(n))
119+
}
120+
121+
done := make(chan struct{})
122+
var wg sync.WaitGroup
123+
defer func() {
124+
close(done)
125+
wg.Wait()
126+
}()
127+
for g := int64(runtime.GOMAXPROCS(0)); g > 0; g-- {
128+
r := rand.New(rand.NewSource(g))
129+
wg.Add(1)
130+
go func(g int64) {
131+
defer wg.Done()
132+
for i := int64(0); ; i++ {
133+
select {
134+
case <-done:
135+
return
136+
default:
137+
}
138+
for n := int64(1); n < mapSize; n++ {
139+
if r.Int63n(mapSize) == 0 {
140+
m.Store(n, n*i*g)
141+
} else {
142+
m.Load(n)
143+
}
144+
}
145+
}
146+
}(g)
147+
}
148+
149+
iters := 1 << 10
150+
if testing.Short() {
151+
iters = 16
152+
}
153+
for n := iters; n > 0; n-- {
154+
seen := make(map[int64]bool, mapSize)
155+
156+
m.Range(func(ki, vi interface{}) bool {
157+
k, v := ki.(int64), vi.(int64)
158+
if v%k != 0 {
159+
t.Fatalf("while Storing multiples of %v, Range saw value %v", k, v)
160+
}
161+
if seen[k] {
162+
t.Fatalf("Range visited key %v twice", k)
163+
}
164+
seen[k] = true
165+
return true
166+
})
167+
168+
if len(seen) != mapSize {
169+
t.Fatalf("Range visited %v elements of %v-element Map", len(seen), mapSize)
170+
}
171+
}
172+
}

0 commit comments

Comments
 (0)