Skip to content

Commit b97329e

Browse files
authored
Fix locking bug in memberlistIter (#17)
1 parent e505ebd commit b97329e

File tree

3 files changed

+34
-34
lines changed

3 files changed

+34
-34
lines changed

swim/memberlist.go

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ type memberlist struct {
5757
// could use members lock for that, but that introduces more deadlocks, so
5858
// making a short-term fix instead by adding another lock. Like said, this
5959
// is short-term, see github#113.
60-
sync.RWMutex
60+
updateLock sync.Mutex
6161
}
6262

6363
// newMemberlist returns a new member list
@@ -84,10 +84,8 @@ func newMemberlist(n *Node, initialLabels LabelMap) *memberlist {
8484

8585
func (m *memberlist) Checksum() uint32 {
8686
m.members.Lock()
87-
checksum := m.members.checksum
88-
m.members.Unlock()
89-
90-
return checksum
87+
defer m.members.Unlock()
88+
return m.members.checksum
9189
}
9290

9391
// computes membership checksum
@@ -114,6 +112,7 @@ func (m *memberlist) ComputeChecksum() {
114112
}
115113

116114
// generates string to use when computing checksum
115+
// call with m.members.lock
117116
func (m *memberlist) genChecksumString() string {
118117
var strings sort.StringSlice
119118
var buffer bytes.Buffer
@@ -142,7 +141,7 @@ func (m *memberlist) genChecksumString() string {
142141
return buffer.String()
143142
}
144143

145-
// returns the member at a specific address
144+
// returns a copy of the member at a specific address
146145
func (m *memberlist) Member(address string) (*Member, bool) {
147146
var memberCopy *Member
148147
m.members.RLock()
@@ -190,21 +189,22 @@ func (m *memberlist) RemoveMember(address string) bool {
190189
return hasMember
191190
}
192191

192+
// returns a copy of the member at an index, or nil if i >= length
193193
func (m *memberlist) MemberAt(i int) *Member {
194194
m.members.RLock()
195+
defer m.members.RUnlock()
196+
if i >= len(m.members.list) {
197+
return nil
198+
}
195199
member := new(Member)
196200
*member = *m.members.list[i]
197-
m.members.RUnlock()
198-
199201
return member
200202
}
201203

202204
func (m *memberlist) NumMembers() int {
203205
m.members.RLock()
204-
n := len(m.members.list)
205-
m.members.RUnlock()
206-
207-
return n
206+
defer m.members.RUnlock()
207+
return len(m.members.list)
208208
}
209209

210210
// returns whether or not a member is pingable
@@ -217,12 +217,12 @@ func (m *memberlist) Pingable(member Member) bool {
217217
// returns the number of pingable members in the memberlist
218218
func (m *memberlist) NumPingableMembers() (n int) {
219219
m.members.RLock()
220+
defer m.members.RUnlock()
220221
for _, member := range m.members.list {
221222
if m.Pingable(*member) {
222223
n++
223224
}
224225
}
225-
m.members.RUnlock()
226226

227227
return n
228228
}
@@ -232,6 +232,8 @@ func (m *memberlist) RandomPingableMembers(n int, excluding map[string]bool) []M
232232
members := make([]Member, 0, n)
233233

234234
m.members.RLock()
235+
defer m.members.RUnlock()
236+
235237
indices := rand.Perm(len(m.members.list))
236238
for _, index := range indices {
237239
member := m.members.list[index]
@@ -242,22 +244,21 @@ func (m *memberlist) RandomPingableMembers(n int, excluding map[string]bool) []M
242244
}
243245
}
244246
}
245-
m.members.RUnlock()
246247
return members
247248
}
248249

249250
// returns an slice of (copied) members representing the current state of the
250251
// membership. The membership will be filtered by the predicates provided.
251252
func (m *memberlist) GetMembers(predicates ...MemberPredicate) (members []Member) {
252253
m.members.RLock()
254+
defer m.members.RUnlock()
255+
253256
members = make([]Member, 0, len(m.members.list))
254257
for _, member := range m.members.list {
255258
if MemberMatchesPredicates(*member, predicates...) {
256259
members = append(members, *member)
257260
}
258261
}
259-
m.members.RUnlock()
260-
261262
return
262263
}
263264

@@ -312,8 +313,8 @@ func (m *memberlist) SetLocalLabel(key, value string) error {
312313
// argument indicates if the key was present on the node or not
313314
func (m *memberlist) GetLocalLabel(key string) (string, bool) {
314315
m.members.RLock()
316+
defer m.members.RUnlock()
315317
value, has := m.local.Labels[key]
316-
m.members.RUnlock()
317318
return value, has
318319
}
319320

@@ -531,7 +532,7 @@ func (m *memberlist) Update(changes []Change) (applied []Change) {
531532

532533
var memberChanges []membership.MemberChange
533534

534-
m.Lock()
535+
m.updateLock.Lock()
535536

536537
// run through all changes received and figure out if they need to be accepted
537538
m.members.Lock()
@@ -625,7 +626,7 @@ func (m *memberlist) Update(changes []Change) (applied []Change) {
625626
})
626627
}
627628

628-
m.Unlock()
629+
m.updateLock.Unlock()
629630
return applied
630631
}
631632

@@ -659,15 +660,15 @@ func (m *memberlist) getJoinPosition() int {
659660
// shuffles the member list
660661
func (m *memberlist) Shuffle() {
661662
m.members.Lock()
663+
defer m.members.Unlock()
662664
m.members.list = shuffle(m.members.list)
663-
m.members.Unlock()
664665
}
665666

666667
// String returns a JSON string
667668
func (m *memberlist) String() string {
668669
m.members.RLock()
670+
defer m.members.RUnlock()
669671
str, _ := json.Marshal(m.members.list) // will never return error (presumably)
670-
m.members.RUnlock()
671672
return string(str)
672673
}
673674

@@ -682,12 +683,13 @@ func (m *memberlist) CountMembers(predicates ...MemberPredicate) int {
682683
count := 0
683684

684685
m.members.RLock()
686+
defer m.members.RUnlock()
687+
685688
for _, member := range m.members.list {
686689
if MemberMatchesPredicates(*member, predicates...) {
687690
count++
688691
}
689692
}
690-
m.members.RUnlock()
691693

692694
return count
693695
}

swim/memberlist_iter.go

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,21 +48,20 @@ func newMemberlistIter(m *memberlist) *memberlistIter {
4848
// Next returns the next pingable member in the member list, if it
4949
// visits all members but none are pingable returns nil, false
5050
func (i *memberlistIter) Next() (*Member, bool) {
51-
maxToVisit := i.m.NumMembers()
52-
visited := make(map[string]bool)
53-
54-
for len(visited) < maxToVisit {
51+
for maxToVisit := i.m.NumMembers(); maxToVisit >= 0; maxToVisit-- {
5552
i.currentIndex++
5653

57-
if i.currentIndex >= i.m.NumMembers() {
54+
member := i.m.MemberAt(i.currentIndex)
55+
if member == nil {
5856
i.currentIndex = 0
5957
i.currentRound++
6058
i.m.Shuffle()
59+
member = i.m.MemberAt(i.currentIndex)
60+
if member == nil {
61+
return nil, false
62+
}
6163
}
6264

63-
member := i.m.MemberAt(i.currentIndex)
64-
visited[member.Address] = true
65-
6665
if i.m.Pingable(*member) {
6766
return member, true
6867
}

swim/node.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ import (
3232

3333
"github.com/benbjohnson/clock"
3434
"github.com/rcrowley/go-metrics"
35-
log "github.com/uber-common/bark"
3635
"github.com/temporalio/ringpop-go/logging"
3736
"github.com/temporalio/ringpop-go/shared"
3837
"github.com/temporalio/ringpop-go/util"
38+
log "github.com/uber-common/bark"
3939
)
4040

4141
var (
@@ -301,9 +301,8 @@ func (n *Node) HasChanges() bool {
301301
func (n *Node) Incarnation() int64 {
302302
if n.memberlist != nil && n.memberlist.local != nil {
303303
n.memberlist.members.RLock()
304-
incarnation := n.memberlist.local.Incarnation
305-
n.memberlist.members.RUnlock()
306-
return incarnation
304+
defer n.memberlist.members.RUnlock()
305+
return n.memberlist.local.Incarnation
307306
}
308307
return -1
309308
}

0 commit comments

Comments
 (0)