Source file
src/sync/map_test.go
1
2
3
4
5 package sync_test
6
7 import (
8 "internal/testenv"
9 "math/rand"
10 "reflect"
11 "runtime"
12 "sync"
13 "sync/atomic"
14 "testing"
15 "testing/quick"
16 )
17
18 type mapOp string
19
20 const (
21 opLoad = mapOp("Load")
22 opStore = mapOp("Store")
23 opLoadOrStore = mapOp("LoadOrStore")
24 opLoadAndDelete = mapOp("LoadAndDelete")
25 opDelete = mapOp("Delete")
26 opSwap = mapOp("Swap")
27 opCompareAndSwap = mapOp("CompareAndSwap")
28 opCompareAndDelete = mapOp("CompareAndDelete")
29 opClear = mapOp("Clear")
30 )
31
32 var mapOps = [...]mapOp{
33 opLoad,
34 opStore,
35 opLoadOrStore,
36 opLoadAndDelete,
37 opDelete,
38 opSwap,
39 opCompareAndSwap,
40 opCompareAndDelete,
41 opClear,
42 }
43
44
45 type mapCall struct {
46 op mapOp
47 k, v any
48 }
49
50 func (c mapCall) apply(m mapInterface) (any, bool) {
51 switch c.op {
52 case opLoad:
53 return m.Load(c.k)
54 case opStore:
55 m.Store(c.k, c.v)
56 return nil, false
57 case opLoadOrStore:
58 return m.LoadOrStore(c.k, c.v)
59 case opLoadAndDelete:
60 return m.LoadAndDelete(c.k)
61 case opDelete:
62 m.Delete(c.k)
63 return nil, false
64 case opSwap:
65 return m.Swap(c.k, c.v)
66 case opCompareAndSwap:
67 if m.CompareAndSwap(c.k, c.v, rand.Int()) {
68 m.Delete(c.k)
69 return c.v, true
70 }
71 return nil, false
72 case opCompareAndDelete:
73 if m.CompareAndDelete(c.k, c.v) {
74 if _, ok := m.Load(c.k); !ok {
75 return nil, true
76 }
77 }
78 return nil, false
79 case opClear:
80 m.Clear()
81 return nil, false
82 default:
83 panic("invalid mapOp")
84 }
85 }
86
87 type mapResult struct {
88 value any
89 ok bool
90 }
91
92 func randValue(r *rand.Rand) any {
93 b := make([]byte, r.Intn(4))
94 for i := range b {
95 b[i] = 'a' + byte(rand.Intn(26))
96 }
97 return string(b)
98 }
99
100 func (mapCall) Generate(r *rand.Rand, size int) reflect.Value {
101 c := mapCall{op: mapOps[rand.Intn(len(mapOps))], k: randValue(r)}
102 switch c.op {
103 case opStore, opLoadOrStore:
104 c.v = randValue(r)
105 }
106 return reflect.ValueOf(c)
107 }
108
109 func applyCalls(m mapInterface, calls []mapCall) (results []mapResult, final map[any]any) {
110 for _, c := range calls {
111 v, ok := c.apply(m)
112 results = append(results, mapResult{v, ok})
113 }
114
115 final = make(map[any]any)
116 m.Range(func(k, v any) bool {
117 final[k] = v
118 return true
119 })
120
121 return results, final
122 }
123
124 func applyMap(calls []mapCall) ([]mapResult, map[any]any) {
125 return applyCalls(new(sync.Map), calls)
126 }
127
128 func applyRWMutexMap(calls []mapCall) ([]mapResult, map[any]any) {
129 return applyCalls(new(RWMutexMap), calls)
130 }
131
132 func applyDeepCopyMap(calls []mapCall) ([]mapResult, map[any]any) {
133 return applyCalls(new(DeepCopyMap), calls)
134 }
135
136 func TestMapMatchesRWMutex(t *testing.T) {
137 if err := quick.CheckEqual(applyMap, applyRWMutexMap, nil); err != nil {
138 t.Error(err)
139 }
140 }
141
142 func TestMapMatchesDeepCopy(t *testing.T) {
143 if err := quick.CheckEqual(applyMap, applyDeepCopyMap, nil); err != nil {
144 t.Error(err)
145 }
146 }
147
148 func TestConcurrentRange(t *testing.T) {
149 const mapSize = 1 << 10
150
151 m := new(sync.Map)
152 for n := int64(1); n <= mapSize; n++ {
153 m.Store(n, int64(n))
154 }
155
156 done := make(chan struct{})
157 var wg sync.WaitGroup
158 defer func() {
159 close(done)
160 wg.Wait()
161 }()
162 for g := int64(runtime.GOMAXPROCS(0)); g > 0; g-- {
163 r := rand.New(rand.NewSource(g))
164 wg.Add(1)
165 go func(g int64) {
166 defer wg.Done()
167 for i := int64(0); ; i++ {
168 select {
169 case <-done:
170 return
171 default:
172 }
173 for n := int64(1); n < mapSize; n++ {
174 if r.Int63n(mapSize) == 0 {
175 m.Store(n, n*i*g)
176 } else {
177 m.Load(n)
178 }
179 }
180 }
181 }(g)
182 }
183
184 iters := 1 << 10
185 if testing.Short() {
186 iters = 16
187 }
188 for n := iters; n > 0; n-- {
189 seen := make(map[int64]bool, mapSize)
190
191 m.Range(func(ki, vi any) bool {
192 k, v := ki.(int64), vi.(int64)
193 if v%k != 0 {
194 t.Fatalf("while Storing multiples of %v, Range saw value %v", k, v)
195 }
196 if seen[k] {
197 t.Fatalf("Range visited key %v twice", k)
198 }
199 seen[k] = true
200 return true
201 })
202
203 if len(seen) != mapSize {
204 t.Fatalf("Range visited %v elements of %v-element Map", len(seen), mapSize)
205 }
206 }
207 }
208
209 func TestIssue40999(t *testing.T) {
210 var m sync.Map
211
212
213
214
215 m.Store(nil, struct{}{})
216
217 var finalized uint32
218
219
220
221 for atomic.LoadUint32(&finalized) == 0 {
222 p := new(int)
223 runtime.SetFinalizer(p, func(*int) {
224 atomic.AddUint32(&finalized, 1)
225 })
226 m.Store(p, struct{}{})
227 m.Delete(p)
228 runtime.GC()
229 }
230 }
231
232 func TestMapRangeNestedCall(t *testing.T) {
233 var m sync.Map
234 for i, v := range [3]string{"hello", "world", "Go"} {
235 m.Store(i, v)
236 }
237 m.Range(func(key, value any) bool {
238 m.Range(func(key, value any) bool {
239
240
241 if v, ok := m.Load(key); !ok || !reflect.DeepEqual(v, value) {
242 t.Fatalf("Nested Range loads unexpected value, got %+v want %+v", v, value)
243 }
244
245
246
247
248 if _, loaded := m.LoadOrStore(42, "dummy"); loaded {
249 t.Fatalf("Nested Range loads unexpected value, want store a new value")
250 }
251
252
253
254
255
256 val := "sync.Map"
257 m.Store(42, val)
258 if v, loaded := m.LoadAndDelete(42); !loaded || !reflect.DeepEqual(v, val) {
259 t.Fatalf("Nested Range loads unexpected value, got %v, want %v", v, val)
260 }
261 return true
262 })
263
264
265 m.Delete(key)
266 return true
267 })
268
269
270
271 length := 0
272 m.Range(func(key, value any) bool {
273 length++
274 return true
275 })
276
277 if length != 0 {
278 t.Fatalf("Unexpected sync.Map size, got %v want %v", length, 0)
279 }
280 }
281
282 func TestCompareAndSwap_NonExistingKey(t *testing.T) {
283 m := &sync.Map{}
284 if m.CompareAndSwap(m, nil, 42) {
285
286 t.Fatalf("CompareAndSwap on a non-existing key succeeded")
287 }
288 }
289
290 func TestMapRangeNoAllocations(t *testing.T) {
291 testenv.SkipIfOptimizationOff(t)
292 var m sync.Map
293 allocs := testing.AllocsPerRun(10, func() {
294 m.Range(func(key, value any) bool {
295 return true
296 })
297 })
298 if allocs > 0 {
299 t.Errorf("AllocsPerRun of m.Range = %v; want 0", allocs)
300 }
301 }
302
303
304
305 func TestConcurrentClear(t *testing.T) {
306 var m sync.Map
307
308 wg := sync.WaitGroup{}
309 wg.Add(30)
310
311
312 for i := 0; i < 10; i++ {
313 go func(k, v int) {
314 defer wg.Done()
315 m.Store(k, v)
316 }(i, i*10)
317 }
318
319
320 for i := 0; i < 10; i++ {
321 go func(k int) {
322 defer wg.Done()
323 if value, ok := m.Load(k); ok {
324 t.Logf("Key: %v, Value: %v\n", k, value)
325 } else {
326 t.Logf("Key: %v not found\n", k)
327 }
328 }(i)
329 }
330
331
332 for i := 0; i < 10; i++ {
333 go func() {
334 defer wg.Done()
335 m.Clear()
336 }()
337 }
338
339 wg.Wait()
340
341 m.Clear()
342
343 m.Range(func(k, v any) bool {
344 t.Errorf("after Clear, Map contains (%v, %v); expected to be empty", k, v)
345
346 return true
347 })
348 }
349
350 func TestMapClearNoAllocations(t *testing.T) {
351 testenv.SkipIfOptimizationOff(t)
352 var m sync.Map
353 allocs := testing.AllocsPerRun(10, func() {
354 m.Clear()
355 })
356 if allocs > 0 {
357 t.Errorf("AllocsPerRun of m.Clear = %v; want 0", allocs)
358 }
359 }
360
View as plain text