/
lock.go
189 lines (154 loc) · 3.16 KB
/
lock.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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
package util
import (
"fmt"
"os"
"sync"
"github.com/sasha-s/go-deadlock"
)
func init() {
switch os.Getenv("KUTIL_LOCK_DEFAULT") {
case "sync":
defaultLockType = SyncLock
case "debug":
defaultLockType = DebugLock
case "mock":
defaultLockType = MockLock
default:
defaultLockType = SyncLock
}
switch os.Getenv("KUTIL_LOCK_DEBUG_ORDER_DETECTION") {
case "true":
deadlock.Opts.DisableLockOrderDetection = true
case "false":
deadlock.Opts.DisableLockOrderDetection = false
}
}
//
// RWLocker
//
type RWLocker interface {
sync.Locker
RLock()
RUnlock()
RLocker() sync.Locker
}
type LockType int
const (
DefaultLock LockType = 0
SyncLock LockType = 1
DebugLock LockType = 2
MockLock LockType = 3
)
var defaultLockType LockType
func NewRWLocker(type_ LockType) RWLocker {
switch type_ {
case DefaultLock:
return NewDefaultRWLocker()
case SyncLock:
return NewSyncRWLocker()
case DebugLock:
return NewDebugRWLocker()
case MockLock:
return NewMockRWLocker()
default:
panic(fmt.Sprintf("unsupported lock type: %d", type_))
}
}
//
// DefaultRWLocker
//
func NewDefaultRWLocker() RWLocker {
switch defaultLockType {
case SyncLock:
return NewSyncRWLocker()
case DebugLock:
return NewDebugRWLocker()
case MockLock:
return NewMockRWLocker()
default:
panic(fmt.Sprintf("unsupported lock type: %d", defaultLockType))
}
}
//
// SyncRWLocker
//
func NewSyncRWLocker() RWLocker {
return new(sync.RWMutex)
}
//
// DebugRWLocker
//
func NewDebugRWLocker() RWLocker {
return new(deadlock.RWMutex)
}
//
// MockLocker
//
type MockLocker struct{}
func NewMockLocker() sync.Locker {
return MockLocker{}
}
// ([sync.Locker] interface)
func (self MockLocker) Lock() {}
// ([sync.Locker] interface)
func (self MockLocker) Unlock() {}
//
// MockRWLocker
//
type MockRWLocker struct {
MockLocker
}
func NewMockRWLocker() RWLocker {
return MockRWLocker{}
}
// ([RWLocker] interface)
func (self MockRWLocker) RLock() {}
// ([RWLocker] interface)
func (self MockRWLocker) RUnlock() {}
// ([RWLocker] interface)
func (self MockRWLocker) RLocker() sync.Locker {
return self
}
//
// LockableEntity
//
type LockableEntity interface {
GetEntityLock() RWLocker
}
// From [LockableEntity] interface.
func GetEntityLock(entity any) RWLocker {
if lockable, ok := entity.(LockableEntity); ok {
return lockable.GetEntityLock()
} else {
return nil
}
}
//
// Ad-hoc locks
//
var adHocLocks sync.Map
// Warning: Because pointers can be re-used after the resource is freed,
// there is no way for us to guarantee ad-hoc locks would not be reused
// Thus this facililty should only be used for objects with a known and managed life span.
func GetAdHocLock(pointer any, type_ LockType) RWLocker {
if pointer == nil {
panic("no ad-hoc lock for nil")
}
if lock, ok := adHocLocks.Load(pointer); ok {
return lock.(RWLocker)
} else {
lock := NewRWLocker(type_)
if existing, loaded := adHocLocks.LoadOrStore(pointer, lock); loaded {
return existing.(RWLocker)
} else {
return lock
}
}
}
func ResetAdHocLocks() {
// See: https://stackoverflow.com/a/49355523
adHocLocks.Range(func(key any, value any) bool {
adHocLocks.Delete(key)
return true
})
}