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 5package sync 6 7import ( 8 "sync/atomic" 9 "unsafe" 10) 11 12// Map is like a Go map[interface{}]interface{} but is safe for concurrent use 13// by multiple goroutines without additional locking or coordination. 14// Loads, stores, and deletes run in amortized constant time. 15// 16// The Map type is specialized. Most code should use a plain Go map instead, 17// with separate locking or coordination, for better type safety and to make it 18// easier to maintain other invariants along with the map content. 19// 20// The Map type is optimized for two common use cases: (1) when the entry for a given 21// key is only ever written once but read many times, as in caches that only grow, 22// or (2) when multiple goroutines read, write, and overwrite entries for disjoint 23// sets of keys. In these two cases, use of a Map may significantly reduce lock 24// contention compared to a Go map paired with a separate Mutex or RWMutex. 25// 26// The zero Map is empty and ready for use. A Map must not be copied after first use. 27type Map struct { 28 mu Mutex 29 30 // read contains the portion of the map's contents that are safe for 31 // concurrent access (with or without mu held). 32 // 33 // The read field itself is always safe to load, but must only be stored with 34 // mu held. 35 // 36 // Entries stored in read may be updated concurrently without mu, but updating 37 // a previously-expunged entry requires that the entry be copied to the dirty 38 // map and unexpunged with mu held. 39 read atomic.Value // readOnly 40 41 // dirty contains the portion of the map's contents that require mu to be 42 // held. To ensure that the dirty map can be promoted to the read map quickly, 43 // it also includes all of the non-expunged entries in the read map. 44 // 45 // Expunged entries are not stored in the dirty map. An expunged entry in the 46 // clean map must be unexpunged and added to the dirty map before a new value 47 // can be stored to it. 48 // 49 // If the dirty map is nil, the next write to the map will initialize it by 50 // making a shallow copy of the clean map, omitting stale entries. 51 dirty map[interface{}]*entry 52 53 // misses counts the number of loads since the read map was last updated that 54 // needed to lock mu to determine whether the key was present. 55 // 56 // Once enough misses have occurred to cover the cost of copying the dirty 57 // map, the dirty map will be promoted to the read map (in the unamended 58 // state) and the next store to the map will make a new dirty copy. 59 misses int 60} 61 62// readOnly is an immutable struct stored atomically in the Map.read field. 63type readOnly struct { 64 m map[interface{}]*entry 65 amended bool // true if the dirty map contains some key not in m. 66} 67 68// expunged is an arbitrary pointer that marks entries which have been deleted 69// from the dirty map. 70var expunged = unsafe.Pointer(new(interface{})) 71 72// An entry is a slot in the map corresponding to a particular key. 73type entry struct { 74 // p points to the interface{} value stored for the entry. 75 // 76 // If p == nil, the entry has been deleted and m.dirty == nil. 77 // 78 // If p == expunged, the entry has been deleted, m.dirty != nil, and the entry 79 // is missing from m.dirty. 80 // 81 // Otherwise, the entry is valid and recorded in m.read.m[key] and, if m.dirty 82 // != nil, in m.dirty[key]. 83 // 84 // An entry can be deleted by atomic replacement with nil: when m.dirty is 85 // next created, it will atomically replace nil with expunged and leave 86 // m.dirty[key] unset. 87 // 88 // An entry's associated value can be updated by atomic replacement, provided 89 // p != expunged. If p == expunged, an entry's associated value can be updated 90 // only after first setting m.dirty[key] = e so that lookups using the dirty 91 // map find the entry. 92 p unsafe.Pointer // *interface{} 93} 94 95func newEntry(i interface{}) *entry { 96 return &entry{p: unsafe.Pointer(&i)} 97} 98 99// Load returns the value stored in the map for a key, or nil if no 100// value is present. 101// The ok result indicates whether value was found in the map. 102func (m *Map) Load(key interface{}) (value interface{}, ok bool) { 103 read, _ := m.read.Load().(readOnly) 104 e, ok := read.m[key] 105 if !ok && read.amended { 106 m.mu.Lock() 107 // Avoid reporting a spurious miss if m.dirty got promoted while we were 108 // blocked on m.mu. (If further loads of the same key will not miss, it's 109 // not worth copying the dirty map for this key.) 110 read, _ = m.read.Load().(readOnly) 111 e, ok = read.m[key] 112 if !ok && read.amended { 113 e, ok = m.dirty[key] 114 // Regardless of whether the entry was present, record a miss: this key 115 // will take the slow path until the dirty map is promoted to the read 116 // map. 117 m.missLocked() 118 } 119 m.mu.Unlock() 120 } 121 if !ok { 122 return nil, false 123 } 124 return e.load() 125} 126 127func (e *entry) load() (value interface{}, ok bool) { 128 p := atomic.LoadPointer(&e.p) 129 if p == nil || p == expunged { 130 return nil, false 131 } 132 return *(*interface{})(p), true 133} 134 135// Store sets the value for a key. 136func (m *Map) Store(key, value interface{}) { 137 read, _ := m.read.Load().(readOnly) 138 if e, ok := read.m[key]; ok && e.tryStore(&value) { 139 return 140 } 141 142 m.mu.Lock() 143 read, _ = m.read.Load().(readOnly) 144 if e, ok := read.m[key]; ok { 145 if e.unexpungeLocked() { 146 // The entry was previously expunged, which implies that there is a 147 // non-nil dirty map and this entry is not in it. 148 m.dirty[key] = e 149 } 150 e.storeLocked(&value) 151 } else if e, ok := m.dirty[key]; ok { 152 e.storeLocked(&value) 153 } else { 154 if !read.amended { 155 // We're adding the first new key to the dirty map. 156 // Make sure it is allocated and mark the read-only map as incomplete. 157 m.dirtyLocked() 158 m.read.Store(readOnly{m: read.m, amended: true}) 159 } 160 m.dirty[key] = newEntry(value) 161 } 162 m.mu.Unlock() 163} 164 165// tryStore stores a value if the entry has not been expunged. 166// 167// If the entry is expunged, tryStore returns false and leaves the entry 168// unchanged. 169func (e *entry) tryStore(i *interface{}) bool { 170 p := atomic.LoadPointer(&e.p) 171 if p == expunged { 172 return false 173 } 174 for { 175 if atomic.CompareAndSwapPointer(&e.p, p, unsafe.Pointer(i)) { 176 return true 177 } 178 p = atomic.LoadPointer(&e.p) 179 if p == expunged { 180 return false 181 } 182 } 183} 184 185// unexpungeLocked ensures that the entry is not marked as expunged. 186// 187// If the entry was previously expunged, it must be added to the dirty map 188// before m.mu is unlocked. 189func (e *entry) unexpungeLocked() (wasExpunged bool) { 190 return atomic.CompareAndSwapPointer(&e.p, expunged, nil) 191} 192 193// storeLocked unconditionally stores a value to the entry. 194// 195// The entry must be known not to be expunged. 196func (e *entry) storeLocked(i *interface{}) { 197 atomic.StorePointer(&e.p, unsafe.Pointer(i)) 198} 199 200// LoadOrStore returns the existing value for the key if present. 201// Otherwise, it stores and returns the given value. 202// The loaded result is true if the value was loaded, false if stored. 203func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool) { 204 // Avoid locking if it's a clean hit. 205 read, _ := m.read.Load().(readOnly) 206 if e, ok := read.m[key]; ok { 207 actual, loaded, ok := e.tryLoadOrStore(value) 208 if ok { 209 return actual, loaded 210 } 211 } 212 213 m.mu.Lock() 214 read, _ = m.read.Load().(readOnly) 215 if e, ok := read.m[key]; ok { 216 if e.unexpungeLocked() { 217 m.dirty[key] = e 218 } 219 actual, loaded, _ = e.tryLoadOrStore(value) 220 } else if e, ok := m.dirty[key]; ok { 221 actual, loaded, _ = e.tryLoadOrStore(value) 222 m.missLocked() 223 } else { 224 if !read.amended { 225 // We're adding the first new key to the dirty map. 226 // Make sure it is allocated and mark the read-only map as incomplete. 227 m.dirtyLocked() 228 m.read.Store(readOnly{m: read.m, amended: true}) 229 } 230 m.dirty[key] = newEntry(value) 231 actual, loaded = value, false 232 } 233 m.mu.Unlock() 234 235 return actual, loaded 236} 237 238// tryLoadOrStore atomically loads or stores a value if the entry is not 239// expunged. 240// 241// If the entry is expunged, tryLoadOrStore leaves the entry unchanged and 242// returns with ok==false. 243func (e *entry) tryLoadOrStore(i interface{}) (actual interface{}, loaded, ok bool) { 244 p := atomic.LoadPointer(&e.p) 245 if p == expunged { 246 return nil, false, false 247 } 248 if p != nil { 249 return *(*interface{})(p), true, true 250 } 251 252 // Copy the interface after the first load to make this method more amenable 253 // to escape analysis: if we hit the "load" path or the entry is expunged, we 254 // shouldn't bother heap-allocating. 255 ic := i 256 for { 257 if atomic.CompareAndSwapPointer(&e.p, nil, unsafe.Pointer(&ic)) { 258 return i, false, true 259 } 260 p = atomic.LoadPointer(&e.p) 261 if p == expunged { 262 return nil, false, false 263 } 264 if p != nil { 265 return *(*interface{})(p), true, true 266 } 267 } 268} 269 270// Delete deletes the value for a key. 271func (m *Map) Delete(key interface{}) { 272 read, _ := m.read.Load().(readOnly) 273 e, ok := read.m[key] 274 if !ok && read.amended { 275 m.mu.Lock() 276 read, _ = m.read.Load().(readOnly) 277 e, ok = read.m[key] 278 if !ok && read.amended { 279 delete(m.dirty, key) 280 } 281 m.mu.Unlock() 282 } 283 if ok { 284 e.delete() 285 } 286} 287 288func (e *entry) delete() (hadValue bool) { 289 for { 290 p := atomic.LoadPointer(&e.p) 291 if p == nil || p == expunged { 292 return false 293 } 294 if atomic.CompareAndSwapPointer(&e.p, p, nil) { 295 return true 296 } 297 } 298} 299 300// Range calls f sequentially for each key and value present in the map. 301// If f returns false, range stops the iteration. 302// 303// Range does not necessarily correspond to any consistent snapshot of the Map's 304// contents: no key will be visited more than once, but if the value for any key 305// is stored or deleted concurrently, Range may reflect any mapping for that key 306// from any point during the Range call. 307// 308// Range may be O(N) with the number of elements in the map even if f returns 309// false after a constant number of calls. 310func (m *Map) Range(f func(key, value interface{}) bool) { 311 // We need to be able to iterate over all of the keys that were already 312 // present at the start of the call to Range. 313 // If read.amended is false, then read.m satisfies that property without 314 // requiring us to hold m.mu for a long time. 315 read, _ := m.read.Load().(readOnly) 316 if read.amended { 317 // m.dirty contains keys not in read.m. Fortunately, Range is already O(N) 318 // (assuming the caller does not break out early), so a call to Range 319 // amortizes an entire copy of the map: we can promote the dirty copy 320 // immediately! 321 m.mu.Lock() 322 read, _ = m.read.Load().(readOnly) 323 if read.amended { 324 read = readOnly{m: m.dirty} 325 m.read.Store(read) 326 m.dirty = nil 327 m.misses = 0 328 } 329 m.mu.Unlock() 330 } 331 332 for k, e := range read.m { 333 v, ok := e.load() 334 if !ok { 335 continue 336 } 337 if !f(k, v) { 338 break 339 } 340 } 341} 342 343func (m *Map) missLocked() { 344 m.misses++ 345 if m.misses < len(m.dirty) { 346 return 347 } 348 m.read.Store(readOnly{m: m.dirty}) 349 m.dirty = nil 350 m.misses = 0 351} 352 353func (m *Map) dirtyLocked() { 354 if m.dirty != nil { 355 return 356 } 357 358 read, _ := m.read.Load().(readOnly) 359 m.dirty = make(map[interface{}]*entry, len(read.m)) 360 for k, e := range read.m { 361 if !e.tryExpungeLocked() { 362 m.dirty[k] = e 363 } 364 } 365} 366 367func (e *entry) tryExpungeLocked() (isExpunged bool) { 368 p := atomic.LoadPointer(&e.p) 369 for p == nil { 370 if atomic.CompareAndSwapPointer(&e.p, nil, expunged) { 371 return true 372 } 373 p = atomic.LoadPointer(&e.p) 374 } 375 return p == expunged 376} 377