193 lines
		
	
	
	
		
			5.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			193 lines
		
	
	
	
		
			5.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 
								 | 
							
								// Copyright 2016 The Go Authors. All rights reserved.
							 | 
						||
| 
								 | 
							
								// Use of this source code is governed by a BSD-style
							 | 
						||
| 
								 | 
							
								// license that can be found in the LICENSE file.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Package syncmap provides a concurrent map implementation.
							 | 
						||
| 
								 | 
							
								// It is a prototype for a proposed addition to the sync package
							 | 
						||
| 
								 | 
							
								// in the standard library.
							 | 
						||
| 
								 | 
							
								// (https://golang.org/issue/18177)
							 | 
						||
| 
								 | 
							
								package syncmap
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import (
							 | 
						||
| 
								 | 
							
									"sync"
							 | 
						||
| 
								 | 
							
									"sync/atomic"
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Map is a concurrent map with amortized-constant-time operations.
							 | 
						||
| 
								 | 
							
								// It is safe for multiple goroutines to call a Map's methods concurrently.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// The zero Map is valid and empty.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// A Map must not be copied after first use.
							 | 
						||
| 
								 | 
							
								type Map struct {
							 | 
						||
| 
								 | 
							
									mu sync.Mutex
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// clean is a copy of the map's contents that will never be overwritten, and
							 | 
						||
| 
								 | 
							
									// is thus safe for concurrent lookups without additional synchronization.
							 | 
						||
| 
								 | 
							
									//
							 | 
						||
| 
								 | 
							
									// A nil clean map indicates that the current map contents are stored in the
							 | 
						||
| 
								 | 
							
									// dirty field instead.
							 | 
						||
| 
								 | 
							
									// If clean is non-nil, its contents are up-to-date.
							 | 
						||
| 
								 | 
							
									//
							 | 
						||
| 
								 | 
							
									// clean is always safe to load, but must only be stored with mu held.
							 | 
						||
| 
								 | 
							
									clean atomic.Value // map[interface{}]interface{}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// dirty is a copy of the map to which all writes occur.
							 | 
						||
| 
								 | 
							
									//
							 | 
						||
| 
								 | 
							
									// A nil dirty map indicates that the current map contents are either empty or
							 | 
						||
| 
								 | 
							
									// stored in the clean field.
							 | 
						||
| 
								 | 
							
									//
							 | 
						||
| 
								 | 
							
									// If the dirty map is nil, the next write to the map will initialize it by
							 | 
						||
| 
								 | 
							
									// making a deep copy of the clean map, then setting the clean map to nil.
							 | 
						||
| 
								 | 
							
									dirty map[interface{}]interface{}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// misses counts the number of Load calls for which the clean map was nil
							 | 
						||
| 
								 | 
							
									// since the last write.
							 | 
						||
| 
								 | 
							
									//
							 | 
						||
| 
								 | 
							
									// Once enough Load misses have occurred to cover the cost of a copy, the
							 | 
						||
| 
								 | 
							
									// dirty map will be promoted to clean and any subsequent writes will make
							 | 
						||
| 
								 | 
							
									// a new copy.
							 | 
						||
| 
								 | 
							
									misses int
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Load returns the value stored in the map for a key, or nil if no
							 | 
						||
| 
								 | 
							
								// value is present.
							 | 
						||
| 
								 | 
							
								// The ok result indicates whether value was found in the map.
							 | 
						||
| 
								 | 
							
								func (m *Map) Load(key interface{}) (value interface{}, ok bool) {
							 | 
						||
| 
								 | 
							
									clean, _ := m.clean.Load().(map[interface{}]interface{})
							 | 
						||
| 
								 | 
							
									if clean != nil {
							 | 
						||
| 
								 | 
							
										value, ok = clean[key]
							 | 
						||
| 
								 | 
							
										return value, ok
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									m.mu.Lock()
							 | 
						||
| 
								 | 
							
									if m.dirty == nil {
							 | 
						||
| 
								 | 
							
										clean, _ := m.clean.Load().(map[interface{}]interface{})
							 | 
						||
| 
								 | 
							
										if clean == nil {
							 | 
						||
| 
								 | 
							
											// Completely empty — promote to clean immediately.
							 | 
						||
| 
								 | 
							
											m.clean.Store(map[interface{}]interface{}{})
							 | 
						||
| 
								 | 
							
										} else {
							 | 
						||
| 
								 | 
							
											value, ok = clean[key]
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										m.mu.Unlock()
							 | 
						||
| 
								 | 
							
										return value, ok
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									value, ok = m.dirty[key]
							 | 
						||
| 
								 | 
							
									m.missLocked()
							 | 
						||
| 
								 | 
							
									m.mu.Unlock()
							 | 
						||
| 
								 | 
							
									return value, ok
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Store sets the value for a key.
							 | 
						||
| 
								 | 
							
								func (m *Map) Store(key, value interface{}) {
							 | 
						||
| 
								 | 
							
									m.mu.Lock()
							 | 
						||
| 
								 | 
							
									m.dirtyLocked()
							 | 
						||
| 
								 | 
							
									m.dirty[key] = value
							 | 
						||
| 
								 | 
							
									m.mu.Unlock()
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// LoadOrStore returns the existing value for the key if present.
							 | 
						||
| 
								 | 
							
								// Otherwise, it stores and returns the given value.
							 | 
						||
| 
								 | 
							
								// The loaded result is true if the value was loaded, false if stored.
							 | 
						||
| 
								 | 
							
								func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool) {
							 | 
						||
| 
								 | 
							
									// Avoid locking if it's a clean hit.
							 | 
						||
| 
								 | 
							
									clean, _ := m.clean.Load().(map[interface{}]interface{})
							 | 
						||
| 
								 | 
							
									actual, loaded = clean[key]
							 | 
						||
| 
								 | 
							
									if loaded {
							 | 
						||
| 
								 | 
							
										return actual, true
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									m.mu.Lock()
							 | 
						||
| 
								 | 
							
									if m.dirty == nil {
							 | 
						||
| 
								 | 
							
										// Reload clean in case it changed while we were waiting on m.mu.
							 | 
						||
| 
								 | 
							
										clean, _ := m.clean.Load().(map[interface{}]interface{})
							 | 
						||
| 
								 | 
							
										actual, loaded = clean[key]
							 | 
						||
| 
								 | 
							
										if loaded {
							 | 
						||
| 
								 | 
							
											m.mu.Unlock()
							 | 
						||
| 
								 | 
							
											return actual, true
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									} else {
							 | 
						||
| 
								 | 
							
										actual, loaded = m.dirty[key]
							 | 
						||
| 
								 | 
							
										if loaded {
							 | 
						||
| 
								 | 
							
											m.missLocked()
							 | 
						||
| 
								 | 
							
											m.mu.Unlock()
							 | 
						||
| 
								 | 
							
											return actual, true
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									m.dirtyLocked()
							 | 
						||
| 
								 | 
							
									m.dirty[key] = value
							 | 
						||
| 
								 | 
							
									actual = value
							 | 
						||
| 
								 | 
							
									m.mu.Unlock()
							 | 
						||
| 
								 | 
							
									return actual, false
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Delete deletes the value for a key.
							 | 
						||
| 
								 | 
							
								func (m *Map) Delete(key interface{}) {
							 | 
						||
| 
								 | 
							
									m.mu.Lock()
							 | 
						||
| 
								 | 
							
									m.dirtyLocked()
							 | 
						||
| 
								 | 
							
									delete(m.dirty, key)
							 | 
						||
| 
								 | 
							
									m.mu.Unlock()
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Range calls f sequentially for each key and value present in the map.
							 | 
						||
| 
								 | 
							
								// If f returns false, range stops the iteration.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// Calls to other Map methods may block until Range returns.
							 | 
						||
| 
								 | 
							
								// The function f must not call any other methods on the Map.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// Range does not necessarily correspond to any consistent snapshot of the Map's
							 | 
						||
| 
								 | 
							
								// contents: no key will be visited more than once, but if the value for any key
							 | 
						||
| 
								 | 
							
								// is stored or deleted concurrently, Range may reflect any mapping for that key
							 | 
						||
| 
								 | 
							
								// from any point during the Range call.
							 | 
						||
| 
								 | 
							
								func (m *Map) Range(f func(key, value interface{}) bool) {
							 | 
						||
| 
								 | 
							
									clean, _ := m.clean.Load().(map[interface{}]interface{})
							 | 
						||
| 
								 | 
							
									if clean == nil {
							 | 
						||
| 
								 | 
							
										m.mu.Lock()
							 | 
						||
| 
								 | 
							
										if m.dirty == nil {
							 | 
						||
| 
								 | 
							
											clean, _ = m.clean.Load().(map[interface{}]interface{})
							 | 
						||
| 
								 | 
							
											if clean == nil {
							 | 
						||
| 
								 | 
							
												// Completely empty — add an empty map to bypass m.mu next time.
							 | 
						||
| 
								 | 
							
												m.clean.Store(map[interface{}]interface{}{})
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										} else {
							 | 
						||
| 
								 | 
							
											// Range is already O(N), so a call to Range amortizes an entire copy of
							 | 
						||
| 
								 | 
							
											// the map.  If it is dirty, we can promote it to clean immediately!
							 | 
						||
| 
								 | 
							
											clean = m.dirty
							 | 
						||
| 
								 | 
							
											m.clean.Store(clean)
							 | 
						||
| 
								 | 
							
											m.dirty = nil
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										m.mu.Unlock()
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									for k, v := range clean {
							 | 
						||
| 
								 | 
							
										if !f(k, v) {
							 | 
						||
| 
								 | 
							
											break
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (m *Map) missLocked() {
							 | 
						||
| 
								 | 
							
									if m.misses++; m.misses >= len(m.dirty) {
							 | 
						||
| 
								 | 
							
										m.clean.Store(m.dirty)
							 | 
						||
| 
								 | 
							
										m.dirty = nil
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// dirtyLocked prepares the map for a subsequent write.
							 | 
						||
| 
								 | 
							
								// It ensures that the dirty field is non-nil and clean is nil by making a deep
							 | 
						||
| 
								 | 
							
								// copy of clean.
							 | 
						||
| 
								 | 
							
								func (m *Map) dirtyLocked() {
							 | 
						||
| 
								 | 
							
									m.misses = 0
							 | 
						||
| 
								 | 
							
									if m.dirty != nil {
							 | 
						||
| 
								 | 
							
										return
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									clean, _ := m.clean.Load().(map[interface{}]interface{})
							 | 
						||
| 
								 | 
							
									m.dirty = make(map[interface{}]interface{}, len(clean))
							 | 
						||
| 
								 | 
							
									for k, v := range clean {
							 | 
						||
| 
								 | 
							
										m.dirty[k] = v
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									m.clean.Store(map[interface{}]interface{}(nil))
							 | 
						||
| 
								 | 
							
								}
							 |