1/* 2Package locker is a simple package to manage named ReadWrite mutexes. These 3appear to be especially useful for synchronizing access to session based 4information in web applications. 5 6The common use case is to use the package level functions, which use a package 7level set of locks (safe to use from multiple goroutines simultaneously). 8However, you may also create a new separate set of locks. 9 10All locks are implemented with read-write mutexes. To use them like a regular 11mutex, simply ignore the RLock/RUnlock functions. 12*/ 13package locker 14 15// BUG(burntsushi): The locker here can grow without bound in long running 16// programs. Since it's intended to be used in web applications, this is a 17// major problem. Figure out a way to keep the locker lean. 18 19import ( 20 "fmt" 21 "sync" 22) 23 24// Locker represents the set of named ReadWrite mutexes. It is safe to access 25// from multiple goroutines simultaneously. 26type Locker struct { 27 locks map[string]*sync.RWMutex 28 locksRW *sync.RWMutex 29} 30 31var locker *Locker 32 33func init() { 34 locker = NewLocker() 35} 36 37func Lock(key string) { locker.Lock(key) } 38func Unlock(key string) { locker.Unlock(key) } 39func RLock(key string) { locker.RLock(key) } 40func RUnlock(key string) { locker.RUnlock(key) } 41 42func NewLocker() *Locker { 43 return &Locker{ 44 locks: make(map[string]*sync.RWMutex), 45 locksRW: new(sync.RWMutex), 46 } 47} 48 49func (lker *Locker) Lock(key string) { 50 lk, ok := lker.getLock(key) 51 if !ok { 52 lk = lker.newLock(key) 53 } 54 lk.Lock() 55} 56 57func (lker *Locker) Unlock(key string) { 58 lk, ok := lker.getLock(key) 59 if !ok { 60 panic(fmt.Errorf("BUG: Lock for key '%s' not initialized.", key)) 61 } 62 lk.Unlock() 63} 64 65func (lker *Locker) RLock(key string) { 66 lk, ok := lker.getLock(key) 67 if !ok { 68 lk = lker.newLock(key) 69 } 70 lk.RLock() 71} 72 73func (lker *Locker) RUnlock(key string) { 74 lk, ok := lker.getLock(key) 75 if !ok { 76 panic(fmt.Errorf("BUG: Lock for key '%s' not initialized.", key)) 77 } 78 lk.RUnlock() 79} 80 81func (lker *Locker) newLock(key string) *sync.RWMutex { 82 lker.locksRW.Lock() 83 defer lker.locksRW.Unlock() 84 85 if lk, ok := lker.locks[key]; ok { 86 return lk 87 } 88 lk := new(sync.RWMutex) 89 lker.locks[key] = lk 90 return lk 91} 92 93func (lker *Locker) getLock(key string) (*sync.RWMutex, bool) { 94 lker.locksRW.RLock() 95 defer lker.locksRW.RUnlock() 96 97 lock, ok := lker.locks[key] 98 return lock, ok 99} 100 101func (lker *Locker) deleteLock(key string) { 102 lker.locksRW.Lock() 103 defer lker.locksRW.Unlock() 104 105 if _, ok := lker.locks[key]; ok { 106 delete(lker.locks, key) 107 } 108} 109