github.com/gitbookio/syncgroup@v0.0.0-20181003125046-3e73b2e6a972/mutexes.go (about)

     1  package syncgroup
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  )
     7  
     8  // MutexGroup provides a group of sync.RWMutex, that be locked/unlocked by key
     9  type MutexGroup struct {
    10  	lock    *sync.RWMutex
    11  	active  *ActiveGroup
    12  	mutexes map[string]*sync.RWMutex
    13  }
    14  
    15  func NewMutexGroup() *MutexGroup {
    16  	return &MutexGroup{
    17  		lock:    &sync.RWMutex{},
    18  		active:  NewActiveGroup(),
    19  		mutexes: map[string]*sync.RWMutex{},
    20  	}
    21  }
    22  
    23  func (mg *MutexGroup) Lock(key string) {
    24  	mg.inc(key)
    25  	mg.getOrCreate(key).Lock()
    26  }
    27  
    28  func (mg *MutexGroup) RLock(key string) {
    29  	mg.inc(key)
    30  	mg.getOrCreate(key).RLock()
    31  }
    32  
    33  func (mg *MutexGroup) Unlock(key string) {
    34  	mg.getOrFail(key).Unlock()
    35  	mg.dec(key)
    36  }
    37  
    38  func (mg *MutexGroup) RUnlock(key string) {
    39  	mg.getOrFail(key).RUnlock()
    40  	mg.dec(key)
    41  }
    42  
    43  func (mg *MutexGroup) Has(key string) bool {
    44  	return mg.active.Has(key)
    45  }
    46  
    47  func (mg *MutexGroup) inc(key string) {
    48  	mg.lock.RLock()
    49  	defer mg.lock.RUnlock()
    50  	mg.active.Inc(key)
    51  }
    52  
    53  func (mg *MutexGroup) dec(key string) {
    54  	mg.lock.Lock()
    55  	defer mg.lock.Unlock()
    56  	// No longer active
    57  	if mg.active.Dec(key) == 0 {
    58  		delete(mg.mutexes, key)
    59  	}
    60  }
    61  
    62  func (mg *MutexGroup) getOrFail(key string) *sync.RWMutex {
    63  	mg.lock.RLock()
    64  	defer mg.lock.RUnlock()
    65  	// Get
    66  	if mutex, ok := mg.mutexes[key]; ok {
    67  		return mutex
    68  	}
    69  	panic(fmt.Sprintf(`MutexGroup.getOrFail("%s"): Tried to perform an Unlock on a key that was never Locked`, key))
    70  }
    71  
    72  func (mg *MutexGroup) getOrCreate(key string) *sync.RWMutex {
    73  	mg.lock.Lock()
    74  	defer mg.lock.Unlock()
    75  
    76  	// Create if doesn't exist
    77  	if _, ok := mg.mutexes[key]; !ok {
    78  		mg.mutexes[key] = &sync.RWMutex{}
    79  	}
    80  
    81  	return mg.mutexes[key]
    82  }