// Entries stored in read may be updated concurrently without mu, but updating
// a previously-expunged entry requires that the entry be copied to the dirty
// map and unexpunged with mu held.
- read atomic.Value // readOnly
+ read atomic.Pointer[readOnly]
// dirty contains the portion of the map's contents that require mu to be
// held. To ensure that the dirty map can be promoted to the read map quickly,
return &entry{p: unsafe.Pointer(&i)}
}
+func (m *Map) loadReadOnly() readOnly {
+ if p := m.read.Load(); p != nil {
+ return *p
+ }
+ return readOnly{}
+}
+
// 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 any) (value any, ok bool) {
- read, _ := m.read.Load().(readOnly)
+ read := m.loadReadOnly()
e, ok := read.m[key]
if !ok && read.amended {
m.mu.Lock()
// Avoid reporting a spurious miss if m.dirty got promoted while we were
// blocked on m.mu. (If further loads of the same key will not miss, it's
// not worth copying the dirty map for this key.)
- read, _ = m.read.Load().(readOnly)
+ read = m.loadReadOnly()
e, ok = read.m[key]
if !ok && read.amended {
e, ok = m.dirty[key]
// Store sets the value for a key.
func (m *Map) Store(key, value any) {
- read, _ := m.read.Load().(readOnly)
+ read := m.loadReadOnly()
if e, ok := read.m[key]; ok && e.tryStore(&value) {
return
}
m.mu.Lock()
- read, _ = m.read.Load().(readOnly)
+ read = m.loadReadOnly()
if e, ok := read.m[key]; ok {
if e.unexpungeLocked() {
// The entry was previously expunged, which implies that there is a
// We're adding the first new key to the dirty map.
// Make sure it is allocated and mark the read-only map as incomplete.
m.dirtyLocked()
- m.read.Store(readOnly{m: read.m, amended: true})
+ m.read.Store(&readOnly{m: read.m, amended: true})
}
m.dirty[key] = newEntry(value)
}
// The loaded result is true if the value was loaded, false if stored.
func (m *Map) LoadOrStore(key, value any) (actual any, loaded bool) {
// Avoid locking if it's a clean hit.
- read, _ := m.read.Load().(readOnly)
+ read := m.loadReadOnly()
if e, ok := read.m[key]; ok {
actual, loaded, ok := e.tryLoadOrStore(value)
if ok {
}
m.mu.Lock()
- read, _ = m.read.Load().(readOnly)
+ read = m.loadReadOnly()
if e, ok := read.m[key]; ok {
if e.unexpungeLocked() {
m.dirty[key] = e
// We're adding the first new key to the dirty map.
// Make sure it is allocated and mark the read-only map as incomplete.
m.dirtyLocked()
- m.read.Store(readOnly{m: read.m, amended: true})
+ m.read.Store(&readOnly{m: read.m, amended: true})
}
m.dirty[key] = newEntry(value)
actual, loaded = value, false
// LoadAndDelete deletes the value for a key, returning the previous value if any.
// The loaded result reports whether the key was present.
func (m *Map) LoadAndDelete(key any) (value any, loaded bool) {
- read, _ := m.read.Load().(readOnly)
+ read := m.loadReadOnly()
e, ok := read.m[key]
if !ok && read.amended {
m.mu.Lock()
- read, _ = m.read.Load().(readOnly)
+ read = m.loadReadOnly()
e, ok = read.m[key]
if !ok && read.amended {
e, ok = m.dirty[key]
// present at the start of the call to Range.
// If read.amended is false, then read.m satisfies that property without
// requiring us to hold m.mu for a long time.
- read, _ := m.read.Load().(readOnly)
+ read := m.loadReadOnly()
if read.amended {
// m.dirty contains keys not in read.m. Fortunately, Range is already O(N)
// (assuming the caller does not break out early), so a call to Range
// amortizes an entire copy of the map: we can promote the dirty copy
// immediately!
m.mu.Lock()
- read, _ = m.read.Load().(readOnly)
+ read = m.loadReadOnly()
if read.amended {
read = readOnly{m: m.dirty}
- m.read.Store(read)
+ m.read.Store(&read)
m.dirty = nil
m.misses = 0
}
if m.misses < len(m.dirty) {
return
}
- m.read.Store(readOnly{m: m.dirty})
+ m.read.Store(&readOnly{m: m.dirty})
m.dirty = nil
m.misses = 0
}
return
}
- read, _ := m.read.Load().(readOnly)
+ read := m.loadReadOnly()
m.dirty = make(map[any]*entry, len(read.m))
for k, e := range read.m {
if !e.tryExpungeLocked() {