Files
seaweedfs/weed/util/concurrent_read_map.go
T
Chris Lu 5f787a25c3 master: survive a volume layout deleted twice (#11098)
* master: survive a layout deleted twice

Two volume servers dropping the last replica of volumes that share a layout
both find it empty and both delete it. The loser's lookup misses, and the
single-value type assertion on the result crashed the master before the
caller could look at the found flag.

Claude-Session: https://claude.ai/code/session_01WmX6Rchx298NQksHDXg7sk

* master: remove a layout and read it back in one step

DeleteVolumeLayout looked the layout up and then deleted it, so two deleters
could each release the lookup ownership of the same layout, or one could find
nothing to release at all. Have the map hand back what it removed.

Claude-Session: https://claude.ai/code/session_01WmX6Rchx298NQksHDXg7sk
2026-09-02 11:50:48 -07:00

65 lines
1.4 KiB
Go

package util
import (
"sync"
)
// A mostly for read map, which can thread-safely
// initialize the map entries.
type ConcurrentReadMap struct {
sync.RWMutex
items map[string]interface{}
}
func NewConcurrentReadMap() *ConcurrentReadMap {
return &ConcurrentReadMap{items: make(map[string]interface{})}
}
func (m *ConcurrentReadMap) initMapEntry(key string, newEntry func() interface{}) (value interface{}) {
m.Lock()
defer m.Unlock()
if value, ok := m.items[key]; ok {
return value
}
value = newEntry()
m.items[key] = value
return value
}
func (m *ConcurrentReadMap) Get(key string, newEntry func() interface{}) interface{} {
m.RLock()
if value, ok := m.items[key]; ok {
m.RUnlock()
return value
}
m.RUnlock()
return m.initMapEntry(key, newEntry)
}
func (m *ConcurrentReadMap) Find(key string) (interface{}, bool) {
m.RLock()
value, ok := m.items[key]
m.RUnlock()
return value, ok
}
func (m *ConcurrentReadMap) Items() (itemsCopy []interface{}) {
m.RLock()
for _, i := range m.items {
itemsCopy = append(itemsCopy, i)
}
m.RUnlock()
return itemsCopy
}
// Delete removes the key and returns what it held, so a caller that has to
// wind the entry down does not race another deleter for it.
func (m *ConcurrentReadMap) Delete(key string) (interface{}, bool) {
m.Lock()
value, ok := m.items[key]
delete(m.items, key)
m.Unlock()
return value, ok
}