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