...
Run Format

Source file src/sync/map.go

Documentation: sync

     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
     6  
     7  import (
     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.
    27  type 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.
    63  type 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.
    70  var expunged = unsafe.Pointer(new(interface{}))
    71  
    72  // An entry is a slot in the map corresponding to a particular key.
    73  type 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  
    95  func 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.
   102  func (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  
   127  func (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.
   136  func (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.
   169  func (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.
   189  func (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.
   196  func (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.
   203  func (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.
   243  func (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.
   271  func (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  
   288  func (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.
   310  func (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  
   343  func (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  
   353  func (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  
   367  func (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  

View as plain text