aboutsummaryrefslogtreecommitdiff
path: root/cmap/map.go
diff options
context:
space:
mode:
Diffstat (limited to 'cmap/map.go')
-rw-r--r--cmap/map.go104
1 files changed, 5 insertions, 99 deletions
diff --git a/cmap/map.go b/cmap/map.go
index bfc0070..7a1fe5b 100644
--- a/cmap/map.go
+++ b/cmap/map.go
@@ -14,7 +14,7 @@ import (
"unsafe"
)
-// Map[K comparable, V comparable] is like a Go map[K]V but is safe for concurrent use
+// Map[K comparable, V any] is like a Go map[K]V but is safe for concurrent use
// by multiple goroutines without additional locking or coordination. Loads,
// stores, and deletes run in amortized constant time.
//
@@ -37,7 +37,7 @@ import (
// and [Map.CompareAndDelete] is a write operation when it returns deleted set to true.
//
// [the Go memory model]: https://go.dev/ref/mem
-type Map[K comparable, V comparable] struct {
+type Map[K comparable, V any] struct {
mu sync.Mutex
// read contains the portion of the map's contents that are safe for
@@ -73,7 +73,7 @@ type Map[K comparable, V comparable] struct {
}
// readOnly is an immutable struct stored atomically in the Map.read field.
-type readOnly[K comparable, V comparable] struct {
+type readOnly[K comparable, V any] struct {
m map[K]*entry[V]
amended bool // true if the dirty map contains some key not in m.
}
@@ -83,7 +83,7 @@ type readOnly[K comparable, V comparable] struct {
var expunged = unsafe.Pointer(new(any))
// An entry is a slot in the map corresponding to a particular key.
-type entry[V comparable] struct {
+type entry[V any] struct {
// p points to the value stored for the entry.
//
// If p == nil, the entry has been deleted, and either m.dirty == nil or
@@ -106,7 +106,7 @@ type entry[V comparable] struct {
p unsafe.Pointer
}
-func newEntry[V comparable](i V) *entry[V] {
+func newEntry[V any](i V) *entry[V] {
return &entry[V]{p: unsafe.Pointer(&i)}
}
@@ -179,33 +179,6 @@ func (m *Map[K, V]) Clear() {
m.misses = 0
}
-// tryCompareAndSwap compare the entry with the given old value and swaps
-// it with a new value if the entry is equal to the old value, and the entry
-// has not been expunged.
-//
-// If the entry is expunged, tryCompareAndSwap returns false and leaves
-// the entry unchanged.
-func (e *entry[V]) tryCompareAndSwap(old V, new V) bool {
- p := atomic.LoadPointer(&e.p)
- if p == nil || p == expunged || *(*V)(p) != old { // XXX
- return false
- }
-
- // Copy the pointer after the first load to make this method more amenable
- // to escape analysis: if the comparison fails from the start, we shouldn't
- // bother heap-allocating a pointer to store.
- nc := new
- for {
- if atomic.CompareAndSwapPointer(&e.p, p, unsafe.Pointer(&nc)) {
- return true
- }
- p = atomic.LoadPointer(&e.p)
- if p == nil || p == expunged || *(*V)(p) != old {
- return false
- }
- }
-}
-
// unexpungeLocked ensures that the entry is not marked as expunged.
//
// If the entry was previously expunged, it must be added to the dirty map
@@ -392,73 +365,6 @@ func (m *Map[K, V]) Swap(key K, value V) (previous V, loaded bool) {
return previous, loaded
}
-// CompareAndSwap swaps the old and new values for key
-// if the value stored in the map is equal to old.
-// The old value must be of a comparable type.
-func (m *Map[K, V]) CompareAndSwap(key K, old, new V) (swapped bool) {
- read := m.loadReadOnly()
- if e, ok := read.m[key]; ok {
- return e.tryCompareAndSwap(old, new)
- } else if !read.amended {
- return false // No existing value for key.
- }
-
- m.mu.Lock()
- defer m.mu.Unlock()
- read = m.loadReadOnly()
- swapped = false
- if e, ok := read.m[key]; ok {
- swapped = e.tryCompareAndSwap(old, new)
- } else if e, ok := m.dirty[key]; ok {
- swapped = e.tryCompareAndSwap(old, new)
- // We needed to lock mu in order to load the entry for key,
- // and the operation didn't change the set of keys in the map
- // (so it would be made more efficient by promoting the dirty
- // map to read-only).
- // Count it as a miss so that we will eventually switch to the
- // more efficient steady state.
- m.missLocked()
- }
- return swapped
-}
-
-// CompareAndDelete deletes the entry for key if its value is equal to old.
-// The old value must be of a comparable type.
-//
-// If there is no current value for key in the map, CompareAndDelete
-// returns false (even if the old value is a nil pointer).
-func (m *Map[K, V]) CompareAndDelete(key K, old V) (deleted bool) {
- read := m.loadReadOnly()
- e, ok := read.m[key]
- if !ok && read.amended {
- m.mu.Lock()
- read = m.loadReadOnly()
- e, ok = read.m[key]
- if !ok && read.amended {
- e, ok = m.dirty[key]
- // Don't delete key from m.dirty: we still need to do the “compare” part
- // of the operation. The entry will eventually be expunged when the
- // dirty map is promoted to the read map.
- //
- // Regardless of whether the entry was present, record a miss: this key
- // will take the slow path until the dirty map is promoted to the read
- // map.
- m.missLocked()
- }
- m.mu.Unlock()
- }
- for ok {
- p := atomic.LoadPointer(&e.p)
- if p == nil || p == expunged || *(*V)(p) != old {
- return false
- }
- if atomic.CompareAndSwapPointer(&e.p, p, nil) {
- return true
- }
- }
- return false
-}
-
// Range calls f sequentially for each key and value present in the map.
// If f returns false, range stops the iteration.
//