-
Notifications
You must be signed in to change notification settings - Fork 116
/
keylock.go
134 lines (113 loc) · 2.5 KB
/
keylock.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package keylock
import (
"fmt"
"sync"
)
// ErrKeyLockNotFound error type for lock object not found
type ErrKeyLockNotFound struct {
// ID unique object identifier.
ID string
}
func (e *ErrKeyLockNotFound) Error() string {
return fmt.Sprintf("Lock with ID: %v not found", e.ID)
}
// ErrInvalidHandle error type for invalid lock handle.
type ErrInvalidHandle struct {
// ID unique object identifier.
ID string
}
func (e *ErrInvalidHandle) Error() string {
return fmt.Sprintf("Invalid Handle with ID: %v", e.ID)
}
// KeyLock is a thread-safe interface for acquiring locks on arbitrary strings.
type KeyLock interface {
// Acquire a lock associated with the specified ID.
// Creates the lock if one doesn't already exist.
Acquire(id string) LockHandle
// Release the lock associated with the specified LockHandle
// Returns an error if it is an invalid LockHandle.
Release(h *LockHandle) error
// Dump all locks.
Dump() []string
}
// LockHandle is an opaque handle to an aquired lock.
type LockHandle struct {
id string
genNum int64
refcnt int64
mutex *sync.Mutex
}
type keyLock struct {
sync.Mutex
lockMap map[string]*LockHandle
}
var (
klLock sync.Mutex
klMap = make(map[string]KeyLock)
)
// New returns a new instance of a KeyLock.
func New() KeyLock {
return &keyLock{lockMap: make(map[string]*LockHandle)}
}
// ByName creates a new instance or returns an existing instance
// if found in the map.
func ByName(klName string) KeyLock {
klLock.Lock()
defer klLock.Unlock()
kl, ok := klMap[klName]
if !ok {
kl = New()
klMap[klName] = kl
}
return kl
}
func (kl *keyLock) Acquire(id string) LockHandle {
h := kl.getOrCreateLock(id)
h.mutex.Lock()
h.genNum++
return *h
}
func (kl *keyLock) Release(h *LockHandle) error {
if len(h.id) == 0 {
return &ErrInvalidHandle{}
}
kl.Lock()
defer kl.Unlock()
lockedH, exists := kl.lockMap[h.id]
if !exists {
return &ErrKeyLockNotFound{ID: h.id}
}
if h.genNum != lockedH.genNum {
return &ErrInvalidHandle{ID: h.id}
}
lockedH.mutex.Unlock()
lockedH.refcnt--
if lockedH.refcnt == 0 {
delete(kl.lockMap, h.id)
}
return nil
}
func (kl *keyLock) Dump() []string {
kl.Lock()
defer kl.Unlock()
keys := make([]string, len(kl.lockMap))
i := 0
for k := range kl.lockMap {
keys[i] = k
}
return keys
}
func (kl *keyLock) getOrCreateLock(id string) *LockHandle {
kl.Lock()
defer kl.Unlock()
h, exists := kl.lockMap[id]
if !exists {
h = &LockHandle{
mutex: &sync.Mutex{},
id: id,
}
kl.lockMap[id] = h
}
h.refcnt++
return h
}