github.com/twelsh-aw/go/src@v0.0.0-20230516233729-a56fe86a7c81/sync/map_test.go (about) 1 // Copyright 2016 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package sync_test 6 7 import ( 8 "math/rand" 9 "reflect" 10 "runtime" 11 "sync" 12 "sync/atomic" 13 "testing" 14 "testing/quick" 15 ) 16 17 type mapOp string 18 19 const ( 20 opLoad = mapOp("Load") 21 opStore = mapOp("Store") 22 opLoadOrStore = mapOp("LoadOrStore") 23 opLoadAndDelete = mapOp("LoadAndDelete") 24 opDelete = mapOp("Delete") 25 opSwap = mapOp("Swap") 26 opCompareAndSwap = mapOp("CompareAndSwap") 27 opCompareAndDelete = mapOp("CompareAndDelete") 28 ) 29 30 var mapOps = [...]mapOp{ 31 opLoad, 32 opStore, 33 opLoadOrStore, 34 opLoadAndDelete, 35 opDelete, 36 opSwap, 37 opCompareAndSwap, 38 opCompareAndDelete, 39 } 40 41 // mapCall is a quick.Generator for calls on mapInterface. 42 type mapCall struct { 43 op mapOp 44 k, v any 45 } 46 47 func (c mapCall) apply(m mapInterface) (any, bool) { 48 switch c.op { 49 case opLoad: 50 return m.Load(c.k) 51 case opStore: 52 m.Store(c.k, c.v) 53 return nil, false 54 case opLoadOrStore: 55 return m.LoadOrStore(c.k, c.v) 56 case opLoadAndDelete: 57 return m.LoadAndDelete(c.k) 58 case opDelete: 59 m.Delete(c.k) 60 return nil, false 61 case opSwap: 62 return m.Swap(c.k, c.v) 63 case opCompareAndSwap: 64 if m.CompareAndSwap(c.k, c.v, rand.Int()) { 65 m.Delete(c.k) 66 return c.v, true 67 } 68 return nil, false 69 case opCompareAndDelete: 70 if m.CompareAndDelete(c.k, c.v) { 71 if _, ok := m.Load(c.k); !ok { 72 return nil, true 73 } 74 } 75 return nil, false 76 default: 77 panic("invalid mapOp") 78 } 79 } 80 81 type mapResult struct { 82 value any 83 ok bool 84 } 85 86 func randValue(r *rand.Rand) any { 87 b := make([]byte, r.Intn(4)) 88 for i := range b { 89 b[i] = 'a' + byte(rand.Intn(26)) 90 } 91 return string(b) 92 } 93 94 func (mapCall) Generate(r *rand.Rand, size int) reflect.Value { 95 c := mapCall{op: mapOps[rand.Intn(len(mapOps))], k: randValue(r)} 96 switch c.op { 97 case opStore, opLoadOrStore: 98 c.v = randValue(r) 99 } 100 return reflect.ValueOf(c) 101 } 102 103 func applyCalls(m mapInterface, calls []mapCall) (results []mapResult, final map[any]any) { 104 for _, c := range calls { 105 v, ok := c.apply(m) 106 results = append(results, mapResult{v, ok}) 107 } 108 109 final = make(map[any]any) 110 m.Range(func(k, v any) bool { 111 final[k] = v 112 return true 113 }) 114 115 return results, final 116 } 117 118 func applyMap(calls []mapCall) ([]mapResult, map[any]any) { 119 return applyCalls(new(sync.Map), calls) 120 } 121 122 func applyRWMutexMap(calls []mapCall) ([]mapResult, map[any]any) { 123 return applyCalls(new(RWMutexMap), calls) 124 } 125 126 func applyDeepCopyMap(calls []mapCall) ([]mapResult, map[any]any) { 127 return applyCalls(new(DeepCopyMap), calls) 128 } 129 130 func TestMapMatchesRWMutex(t *testing.T) { 131 if err := quick.CheckEqual(applyMap, applyRWMutexMap, nil); err != nil { 132 t.Error(err) 133 } 134 } 135 136 func TestMapMatchesDeepCopy(t *testing.T) { 137 if err := quick.CheckEqual(applyMap, applyDeepCopyMap, nil); err != nil { 138 t.Error(err) 139 } 140 } 141 142 func TestConcurrentRange(t *testing.T) { 143 const mapSize = 1 << 10 144 145 m := new(sync.Map) 146 for n := int64(1); n <= mapSize; n++ { 147 m.Store(n, int64(n)) 148 } 149 150 done := make(chan struct{}) 151 var wg sync.WaitGroup 152 defer func() { 153 close(done) 154 wg.Wait() 155 }() 156 for g := int64(runtime.GOMAXPROCS(0)); g > 0; g-- { 157 r := rand.New(rand.NewSource(g)) 158 wg.Add(1) 159 go func(g int64) { 160 defer wg.Done() 161 for i := int64(0); ; i++ { 162 select { 163 case <-done: 164 return 165 default: 166 } 167 for n := int64(1); n < mapSize; n++ { 168 if r.Int63n(mapSize) == 0 { 169 m.Store(n, n*i*g) 170 } else { 171 m.Load(n) 172 } 173 } 174 } 175 }(g) 176 } 177 178 iters := 1 << 10 179 if testing.Short() { 180 iters = 16 181 } 182 for n := iters; n > 0; n-- { 183 seen := make(map[int64]bool, mapSize) 184 185 m.Range(func(ki, vi any) bool { 186 k, v := ki.(int64), vi.(int64) 187 if v%k != 0 { 188 t.Fatalf("while Storing multiples of %v, Range saw value %v", k, v) 189 } 190 if seen[k] { 191 t.Fatalf("Range visited key %v twice", k) 192 } 193 seen[k] = true 194 return true 195 }) 196 197 if len(seen) != mapSize { 198 t.Fatalf("Range visited %v elements of %v-element Map", len(seen), mapSize) 199 } 200 } 201 } 202 203 func TestIssue40999(t *testing.T) { 204 var m sync.Map 205 206 // Since the miss-counting in missLocked (via Delete) 207 // compares the miss count with len(m.dirty), 208 // add an initial entry to bias len(m.dirty) above the miss count. 209 m.Store(nil, struct{}{}) 210 211 var finalized uint32 212 213 // Set finalizers that count for collected keys. A non-zero count 214 // indicates that keys have not been leaked. 215 for atomic.LoadUint32(&finalized) == 0 { 216 p := new(int) 217 runtime.SetFinalizer(p, func(*int) { 218 atomic.AddUint32(&finalized, 1) 219 }) 220 m.Store(p, struct{}{}) 221 m.Delete(p) 222 runtime.GC() 223 } 224 } 225 226 func TestMapRangeNestedCall(t *testing.T) { // Issue 46399 227 var m sync.Map 228 for i, v := range [3]string{"hello", "world", "Go"} { 229 m.Store(i, v) 230 } 231 m.Range(func(key, value any) bool { 232 m.Range(func(key, value any) bool { 233 // We should be able to load the key offered in the Range callback, 234 // because there are no concurrent Delete involved in this tested map. 235 if v, ok := m.Load(key); !ok || !reflect.DeepEqual(v, value) { 236 t.Fatalf("Nested Range loads unexpected value, got %+v want %+v", v, value) 237 } 238 239 // We didn't keep 42 and a value into the map before, if somehow we loaded 240 // a value from such a key, meaning there must be an internal bug regarding 241 // nested range in the Map. 242 if _, loaded := m.LoadOrStore(42, "dummy"); loaded { 243 t.Fatalf("Nested Range loads unexpected value, want store a new value") 244 } 245 246 // Try to Store then LoadAndDelete the corresponding value with the key 247 // 42 to the Map. In this case, the key 42 and associated value should be 248 // removed from the Map. Therefore any future range won't observe key 42 249 // as we checked in above. 250 val := "sync.Map" 251 m.Store(42, val) 252 if v, loaded := m.LoadAndDelete(42); !loaded || !reflect.DeepEqual(v, val) { 253 t.Fatalf("Nested Range loads unexpected value, got %v, want %v", v, val) 254 } 255 return true 256 }) 257 258 // Remove key from Map on-the-fly. 259 m.Delete(key) 260 return true 261 }) 262 263 // After a Range of Delete, all keys should be removed and any 264 // further Range won't invoke the callback. Hence length remains 0. 265 length := 0 266 m.Range(func(key, value any) bool { 267 length++ 268 return true 269 }) 270 271 if length != 0 { 272 t.Fatalf("Unexpected sync.Map size, got %v want %v", length, 0) 273 } 274 } 275 276 func TestCompareAndSwap_NonExistingKey(t *testing.T) { 277 m := &sync.Map{} 278 if m.CompareAndSwap(m, nil, 42) { 279 // See https://go.dev/issue/51972#issuecomment-1126408637. 280 t.Fatalf("CompareAndSwap on an non-existing key succeeded") 281 } 282 }