引用计数资源的线程安全映射

On managing a collection of resources which are:

  1. accessible by name through a global list (e.g. a hash-map)
  2. accessed concurrently from multiple threads
  3. ref-counted (Golang lacks "weak references"; see
    https://groups.google.com/forum/#!topic/golang-nuts/PYWxjT2v6ps)

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.