github.com/twelsh-aw/go/src@v0.0.0-20230516233729-a56fe86a7c81/sync/map_reference_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 "sync" 9 "sync/atomic" 10 ) 11 12 // This file contains reference map implementations for unit-tests. 13 14 // mapInterface is the interface Map implements. 15 type mapInterface interface { 16 Load(any) (any, bool) 17 Store(key, value any) 18 LoadOrStore(key, value any) (actual any, loaded bool) 19 LoadAndDelete(key any) (value any, loaded bool) 20 Delete(any) 21 Swap(key, value any) (previous any, loaded bool) 22 CompareAndSwap(key, old, new any) (swapped bool) 23 CompareAndDelete(key, old any) (deleted bool) 24 Range(func(key, value any) (shouldContinue bool)) 25 } 26 27 var ( 28 _ mapInterface = &RWMutexMap{} 29 _ mapInterface = &DeepCopyMap{} 30 ) 31 32 // RWMutexMap is an implementation of mapInterface using a sync.RWMutex. 33 type RWMutexMap struct { 34 mu sync.RWMutex 35 dirty map[any]any 36 } 37 38 func (m *RWMutexMap) Load(key any) (value any, ok bool) { 39 m.mu.RLock() 40 value, ok = m.dirty[key] 41 m.mu.RUnlock() 42 return 43 } 44 45 func (m *RWMutexMap) Store(key, value any) { 46 m.mu.Lock() 47 if m.dirty == nil { 48 m.dirty = make(map[any]any) 49 } 50 m.dirty[key] = value 51 m.mu.Unlock() 52 } 53 54 func (m *RWMutexMap) LoadOrStore(key, value any) (actual any, loaded bool) { 55 m.mu.Lock() 56 actual, loaded = m.dirty[key] 57 if !loaded { 58 actual = value 59 if m.dirty == nil { 60 m.dirty = make(map[any]any) 61 } 62 m.dirty[key] = value 63 } 64 m.mu.Unlock() 65 return actual, loaded 66 } 67 68 func (m *RWMutexMap) Swap(key, value any) (previous any, loaded bool) { 69 m.mu.Lock() 70 if m.dirty == nil { 71 m.dirty = make(map[any]any) 72 } 73 74 previous, loaded = m.dirty[key] 75 m.dirty[key] = value 76 m.mu.Unlock() 77 return 78 } 79 80 func (m *RWMutexMap) LoadAndDelete(key any) (value any, loaded bool) { 81 m.mu.Lock() 82 value, loaded = m.dirty[key] 83 if !loaded { 84 m.mu.Unlock() 85 return nil, false 86 } 87 delete(m.dirty, key) 88 m.mu.Unlock() 89 return value, loaded 90 } 91 92 func (m *RWMutexMap) Delete(key any) { 93 m.mu.Lock() 94 delete(m.dirty, key) 95 m.mu.Unlock() 96 } 97 98 func (m *RWMutexMap) CompareAndSwap(key, old, new any) (swapped bool) { 99 m.mu.Lock() 100 defer m.mu.Unlock() 101 if m.dirty == nil { 102 return false 103 } 104 105 value, loaded := m.dirty[key] 106 if loaded && value == old { 107 m.dirty[key] = new 108 return true 109 } 110 return false 111 } 112 113 func (m *RWMutexMap) CompareAndDelete(key, old any) (deleted bool) { 114 m.mu.Lock() 115 defer m.mu.Unlock() 116 if m.dirty == nil { 117 return false 118 } 119 120 value, loaded := m.dirty[key] 121 if loaded && value == old { 122 delete(m.dirty, key) 123 return true 124 } 125 return false 126 } 127 128 func (m *RWMutexMap) Range(f func(key, value any) (shouldContinue bool)) { 129 m.mu.RLock() 130 keys := make([]any, 0, len(m.dirty)) 131 for k := range m.dirty { 132 keys = append(keys, k) 133 } 134 m.mu.RUnlock() 135 136 for _, k := range keys { 137 v, ok := m.Load(k) 138 if !ok { 139 continue 140 } 141 if !f(k, v) { 142 break 143 } 144 } 145 } 146 147 // DeepCopyMap is an implementation of mapInterface using a Mutex and 148 // atomic.Value. It makes deep copies of the map on every write to avoid 149 // acquiring the Mutex in Load. 150 type DeepCopyMap struct { 151 mu sync.Mutex 152 clean atomic.Value 153 } 154 155 func (m *DeepCopyMap) Load(key any) (value any, ok bool) { 156 clean, _ := m.clean.Load().(map[any]any) 157 value, ok = clean[key] 158 return value, ok 159 } 160 161 func (m *DeepCopyMap) Store(key, value any) { 162 m.mu.Lock() 163 dirty := m.dirty() 164 dirty[key] = value 165 m.clean.Store(dirty) 166 m.mu.Unlock() 167 } 168 169 func (m *DeepCopyMap) LoadOrStore(key, value any) (actual any, loaded bool) { 170 clean, _ := m.clean.Load().(map[any]any) 171 actual, loaded = clean[key] 172 if loaded { 173 return actual, loaded 174 } 175 176 m.mu.Lock() 177 // Reload clean in case it changed while we were waiting on m.mu. 178 clean, _ = m.clean.Load().(map[any]any) 179 actual, loaded = clean[key] 180 if !loaded { 181 dirty := m.dirty() 182 dirty[key] = value 183 actual = value 184 m.clean.Store(dirty) 185 } 186 m.mu.Unlock() 187 return actual, loaded 188 } 189 190 func (m *DeepCopyMap) Swap(key, value any) (previous any, loaded bool) { 191 m.mu.Lock() 192 dirty := m.dirty() 193 previous, loaded = dirty[key] 194 dirty[key] = value 195 m.clean.Store(dirty) 196 m.mu.Unlock() 197 return 198 } 199 200 func (m *DeepCopyMap) LoadAndDelete(key any) (value any, loaded bool) { 201 m.mu.Lock() 202 dirty := m.dirty() 203 value, loaded = dirty[key] 204 delete(dirty, key) 205 m.clean.Store(dirty) 206 m.mu.Unlock() 207 return 208 } 209 210 func (m *DeepCopyMap) Delete(key any) { 211 m.mu.Lock() 212 dirty := m.dirty() 213 delete(dirty, key) 214 m.clean.Store(dirty) 215 m.mu.Unlock() 216 } 217 218 func (m *DeepCopyMap) CompareAndSwap(key, old, new any) (swapped bool) { 219 clean, _ := m.clean.Load().(map[any]any) 220 if previous, ok := clean[key]; !ok || previous != old { 221 return false 222 } 223 224 m.mu.Lock() 225 defer m.mu.Unlock() 226 dirty := m.dirty() 227 value, loaded := dirty[key] 228 if loaded && value == old { 229 dirty[key] = new 230 m.clean.Store(dirty) 231 return true 232 } 233 return false 234 } 235 236 func (m *DeepCopyMap) CompareAndDelete(key, old any) (deleted bool) { 237 clean, _ := m.clean.Load().(map[any]any) 238 if previous, ok := clean[key]; !ok || previous != old { 239 return false 240 } 241 242 m.mu.Lock() 243 defer m.mu.Unlock() 244 245 dirty := m.dirty() 246 value, loaded := dirty[key] 247 if loaded && value == old { 248 delete(dirty, key) 249 m.clean.Store(dirty) 250 return true 251 } 252 return false 253 } 254 255 func (m *DeepCopyMap) Range(f func(key, value any) (shouldContinue bool)) { 256 clean, _ := m.clean.Load().(map[any]any) 257 for k, v := range clean { 258 if !f(k, v) { 259 break 260 } 261 } 262 } 263 264 func (m *DeepCopyMap) dirty() map[any]any { 265 clean, _ := m.clean.Load().(map[any]any) 266 dirty := make(map[any]any, len(clean)+1) 267 for k, v := range clean { 268 dirty[k] = v 269 } 270 return dirty 271 }