On managing a collection of resources which are:
Example:
var theList tMap // global
// in thread A, B, C etc
aThing := theList.ref("aThing") // if exists then refcount++, else insert
// use aThing
theList.unref("aThing") // refcount--, if 0 then remove
Edit: I expected to find a recommended pattern for this, but nothing turned up. So I came up with this:
type tMap struct {
set map[string]*tMapSet
updt sync.RWMutex
}
type tMapSet struct {
...
refcount int32
}
func (o *tMap) ref(iId string) *tMapSet {
o.updt.RLock()
aSet := o.set[iId]
if aSet != nil {
atomic.AddInt32(&aSet.refcount, 1)
}
o.updt.RUnlock()
if aSet == nil {
o.updt.Lock()
if aTemp := o.set[iId]; aTemp != nil {
aSet = aTemp
aSet.refcount++
} else {
aSet = &tMapSet{refcount:1}
o.set[iId] = aSet
}
o.updt.Unlock()
}
return aSet
}
func (o *tMap) unref(iId string) {
o.updt.RLock()
aSet := o.set[iId]
aN := atomic.AddInt32(&aSet.refcount, -1) // crash if set[iId] not found
o.updt.RUnlock()
if aN == 0 {
o.updt.Lock()
if aSet.refcount == 0 {
delete(o.set, iId)
}
o.updt.Unlock()
}
}
Suggestions improving the clarity or brevity of the above?
Just wrap your map in a mutex to protect access (you could also use RWMutex if lots of reads and few writes, probably data should store a concrete type too). Something like this with a few methods would be fine:
type MagicMap struct {
sync.Mutex
data map[string]interface{}
counts map[string]int
}
func (m MagicMap) Get(key string) interface{} {
m.Lock()
defer m.Unlock()
return m.data[key]
}
func (m MagicMap) Add(key string, value interface{}) {
m.Lock()
m.data[key] = value
m.counts[key] = m.counts[key] + 1
m.Unlock()
}
func (m MagicMap) Remove(key string) {
m.Lock()
count := m.counts[key]
count -= 1
if count < 1 {
delete(m.data, key)
delete(m.counts, key)
} else {
m.counts[key] = count
}
m.Unlock()
}
This is untested, written quickly, and may be buggy, but hopefully gives you a direction to try out. If you want you could just have one map with map[mystruct]int to store structs and counts. The example has separate keys and values.
https://play.golang.org/p/9k1lNRpqua
Here's another example of using a mutex to protect map access:
https://gobyexample.com/mutexes
You can also use the new sync.Map in Go 1.9 but it's slower than just using a mutex.