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 }