github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/util/syncutil/int_map_test.go (about) 1 // Copyright 2019 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 // Copyright 2016 The Go Authors. All rights reserved. 12 // Use of this source code is governed by a BSD-style 13 // license that can be found in licenses/BSD-golang.txt. 14 15 // This code originated in Go's sync package. 16 17 package syncutil 18 19 import ( 20 "math/rand" 21 "reflect" 22 "runtime" 23 "sync" 24 "testing" 25 "testing/quick" 26 "unsafe" 27 ) 28 29 type mapOp string 30 31 const ( 32 opLoad = mapOp("Load") 33 opStore = mapOp("Store") 34 opLoadOrStore = mapOp("LoadOrStore") 35 opDelete = mapOp("Delete") 36 ) 37 38 var mapOps = [...]mapOp{opLoad, opStore, opLoadOrStore, opDelete} 39 40 // mapCall is a quick.Generator for calls on mapInterface. 41 type mapCall struct { 42 op mapOp 43 k int64 44 v unsafe.Pointer 45 } 46 47 func (c mapCall) apply(m mapInterface) (unsafe.Pointer, 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 opDelete: 57 m.Delete(c.k) 58 return nil, false 59 default: 60 panic("invalid mapOp") 61 } 62 } 63 64 type mapResult struct { 65 value interface{} 66 ok bool 67 } 68 69 func randValue(r *rand.Rand) unsafe.Pointer { 70 i := new(int) 71 *i = r.Int() 72 return unsafe.Pointer(i) 73 } 74 75 func (mapCall) Generate(r *rand.Rand, size int) reflect.Value { 76 c := mapCall{op: mapOps[rand.Intn(len(mapOps))], k: r.Int63()} 77 switch c.op { 78 case opStore, opLoadOrStore: 79 c.v = randValue(r) 80 } 81 return reflect.ValueOf(c) 82 } 83 84 func applyCalls( 85 m mapInterface, calls []mapCall, 86 ) (results []mapResult, final map[interface{}]interface{}) { 87 for _, c := range calls { 88 v, ok := c.apply(m) 89 results = append(results, mapResult{v, ok}) 90 } 91 92 final = make(map[interface{}]interface{}) 93 m.Range(func(k int64, v unsafe.Pointer) bool { 94 final[k] = v 95 return true 96 }) 97 98 return results, final 99 } 100 101 func applyMap(calls []mapCall) ([]mapResult, map[interface{}]interface{}) { 102 return applyCalls(new(IntMap), calls) 103 } 104 105 func applyRWMutexMap(calls []mapCall) ([]mapResult, map[interface{}]interface{}) { 106 return applyCalls(new(RWMutexMap), calls) 107 } 108 109 func applyDeepCopyMap(calls []mapCall) ([]mapResult, map[interface{}]interface{}) { 110 return applyCalls(new(DeepCopyMap), calls) 111 } 112 113 func TestMapMatchesRWMutex(t *testing.T) { 114 if err := quick.CheckEqual(applyMap, applyRWMutexMap, nil); err != nil { 115 t.Error(err) 116 } 117 } 118 119 func TestMapMatchesDeepCopy(t *testing.T) { 120 if err := quick.CheckEqual(applyMap, applyDeepCopyMap, nil); err != nil { 121 t.Error(err) 122 } 123 } 124 125 func TestConcurrentRange(t *testing.T) { 126 const mapSize = 1 << 10 127 128 m := new(IntMap) 129 for n := int64(1); n <= mapSize; n++ { 130 v := new(int64) 131 *v = n 132 m.Store(n, unsafe.Pointer(v)) 133 } 134 135 done := make(chan struct{}) 136 var wg sync.WaitGroup 137 defer func() { 138 close(done) 139 wg.Wait() 140 }() 141 for g := int64(runtime.GOMAXPROCS(0)); g > 0; g-- { 142 r := rand.New(rand.NewSource(g)) 143 wg.Add(1) 144 go func(g int64) { 145 defer wg.Done() 146 for i := int64(0); ; i++ { 147 select { 148 case <-done: 149 return 150 default: 151 } 152 for n := int64(1); n < mapSize; n++ { 153 if r.Int63n(mapSize) == 0 { 154 v := new(int64) 155 *v = n * i * g 156 m.Store(n, unsafe.Pointer(v)) 157 } else { 158 m.Load(n) 159 } 160 } 161 } 162 }(g) 163 } 164 165 iters := 1 << 10 166 if testing.Short() { 167 iters = 16 168 } 169 for n := iters; n > 0; n-- { 170 seen := make(map[int64]bool, mapSize) 171 172 m.Range(func(ki int64, vi unsafe.Pointer) bool { 173 k, v := ki, *(*int64)(vi) 174 if v%k != 0 { 175 t.Fatalf("while Storing multiples of %v, Range saw value %v", k, v) 176 } 177 if seen[k] { 178 t.Fatalf("Range visited key %v twice", k) 179 } 180 seen[k] = true 181 return true 182 }) 183 184 if len(seen) != mapSize { 185 t.Fatalf("Range visited %v elements of %v-element Map", len(seen), mapSize) 186 } 187 } 188 }