forked from superfly/litefs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrwmutex.go
185 lines (155 loc) · 4.53 KB
/
rwmutex.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
package litefs
import (
"fmt"
"sync"
)
// RWMutex is a reader/writer mutual exclusion lock. It wraps the sync package
// to provide additional capabilities such as lock upgrades & downgrades. It
// only supports TryLock() & TryRLock() as that is what's supported by our
// FUSE file system.
type RWMutex struct {
mu sync.Mutex
sharedN int // number of readers
excl *RWMutexGuard // exclusive lock holder
}
// State returns whether the mutex has a exclusive lock, one or more shared
// locks, or if the mutex is unlocked.
func (rw *RWMutex) State() RWMutexState {
rw.mu.Lock()
defer rw.mu.Unlock()
if rw.excl != nil {
return RWMutexStateExclusive
} else if rw.sharedN > 0 {
return RWMutexStateShared
}
return RWMutexStateUnlocked
}
// TryLock tries to lock the mutex for writing and returns a guard if it succeeds.
func (rw *RWMutex) TryLock() *RWMutexGuard {
rw.mu.Lock()
defer rw.mu.Unlock()
if !rw.canLock() {
return nil
}
guard := newRWMutexGuard(rw, RWMutexStateExclusive)
rw.excl = guard
return guard
}
// CanLock returns true if the write lock could be acquired.
func (rw *RWMutex) CanLock() bool {
rw.mu.Lock()
defer rw.mu.Unlock()
return rw.canLock()
}
func (rw *RWMutex) canLock() bool {
return rw.sharedN == 0 && rw.excl == nil
}
// TryRLock tries to lock rw for reading and reports whether it succeeded.
func (rw *RWMutex) TryRLock() *RWMutexGuard {
rw.mu.Lock()
defer rw.mu.Unlock()
if !rw.canRLock() {
return nil
}
g := newRWMutexGuard(rw, RWMutexStateShared)
rw.sharedN++
return g
}
// CanRLock returns true if the read lock could be acquired.
func (rw *RWMutex) CanRLock() bool {
rw.mu.Lock()
defer rw.mu.Unlock()
return rw.canRLock()
}
func (rw *RWMutex) canRLock() bool {
return rw.excl == nil
}
// RWMutexGuard is a reference to a held lock.
type RWMutexGuard struct {
rw *RWMutex
state RWMutexState
}
func newRWMutexGuard(rw *RWMutex, state RWMutexState) *RWMutexGuard {
return &RWMutexGuard{
rw: rw,
state: state,
}
}
// TryLock upgrades the lock from a shared lock to an exclusive lock.
// This is a no-op if the lock is already an exclusive lock.
func (g *RWMutexGuard) TryLock() bool {
g.rw.mu.Lock()
defer g.rw.mu.Unlock()
assert(g.state != RWMutexStateUnlocked, "attempted exclusive lock of unlocked guard")
switch g.state {
case RWMutexStateShared:
assert(g.rw.excl == nil, "exclusive lock already held while upgrading shared lock")
if g.rw.sharedN > 1 {
return false // another shared lock is being held
}
assert(g.rw.sharedN == 1, "invalid shared lock count on guard upgrade")
g.rw.sharedN, g.rw.excl = 0, g
g.state = RWMutexStateExclusive
return true
case RWMutexStateExclusive:
return true // no-op
default:
panic(fmt.Sprintf("invalid guard state: %d", g.state))
}
}
// CanLock returns true if the guard can become an exclusive lock.
func (g *RWMutexGuard) CanLock() bool {
g.rw.mu.Lock()
defer g.rw.mu.Unlock()
assert(g.state != RWMutexStateUnlocked, "attempted exclusive lock check of unlocked guard")
switch g.state {
case RWMutexStateShared:
return g.rw.sharedN == 1
case RWMutexStateExclusive:
return true
default:
panic(fmt.Sprintf("invalid guard state: %d", g.state))
}
}
// RLock downgrades the lock from an exclusive lock to a shared lock.
// This is a no-op if the lock is already a shared lock.
func (g *RWMutexGuard) RLock() {
g.rw.mu.Lock()
defer g.rw.mu.Unlock()
assert(g.state != RWMutexStateUnlocked, "attempted shared lock of unlocked guard")
switch g.state {
case RWMutexStateShared:
return // no-op
case RWMutexStateExclusive:
assert(g.rw.excl == g, "attempted downgrade of non-exclusive guard")
g.rw.sharedN, g.rw.excl = 1, nil
g.state = RWMutexStateShared
default:
panic(fmt.Sprintf("invalid guard state: %d", g.state))
}
}
// Unlock unlocks the underlying mutex. Guard must be discarded after Unlock().
func (g *RWMutexGuard) Unlock() {
g.rw.mu.Lock()
defer g.rw.mu.Unlock()
assert(g.state != RWMutexStateUnlocked, "attempted unlock of unlocked guard")
switch g.state {
case RWMutexStateShared:
assert(g.rw.sharedN > 0, "invalid shared lock state on unlock")
g.rw.sharedN--
g.state = RWMutexStateUnlocked
case RWMutexStateExclusive:
assert(g.rw.excl == g, "attempted unlock of non-exclusive guard")
g.rw.sharedN, g.rw.excl = 0, nil
g.state = RWMutexStateUnlocked
default:
panic(fmt.Sprintf("invalid guard state: %d", g.state))
}
}
// RWMutexState represents the lock state of an RWMutexGuard.
type RWMutexState int
const (
RWMutexStateUnlocked = iota
RWMutexStateShared
RWMutexStateExclusive
)