fix bitArray nil bugs

This commit is contained in:
Jae Kwon 2015-05-07 17:35:58 -07:00
parent ba17961269
commit 6020223e85
2 changed files with 70 additions and 33 deletions

View File

@ -21,14 +21,19 @@ func NewBitArray(bits uint) *BitArray {
} }
func (bA *BitArray) Size() uint { func (bA *BitArray) Size() uint {
if bA == nil {
return 0
}
return bA.Bits return bA.Bits
} }
// NOTE: behavior is undefined if i >= bA.Bits // NOTE: behavior is undefined if i >= bA.Bits
func (bA *BitArray) GetIndex(i uint) bool { func (bA *BitArray) GetIndex(i uint) bool {
if bA == nil {
return false
}
bA.mtx.Lock() bA.mtx.Lock()
defer bA.mtx.Unlock() defer bA.mtx.Unlock()
return bA.getIndex(i) return bA.getIndex(i)
} }
@ -41,9 +46,11 @@ func (bA *BitArray) getIndex(i uint) bool {
// NOTE: behavior is undefined if i >= bA.Bits // NOTE: behavior is undefined if i >= bA.Bits
func (bA *BitArray) SetIndex(i uint, v bool) bool { func (bA *BitArray) SetIndex(i uint, v bool) bool {
if bA == nil {
return false
}
bA.mtx.Lock() bA.mtx.Lock()
defer bA.mtx.Unlock() defer bA.mtx.Unlock()
return bA.setIndex(i, v) return bA.setIndex(i, v)
} }
@ -60,6 +67,9 @@ func (bA *BitArray) setIndex(i uint, v bool) bool {
} }
func (bA *BitArray) Copy() *BitArray { func (bA *BitArray) Copy() *BitArray {
if bA == nil {
return nil
}
bA.mtx.Lock() bA.mtx.Lock()
defer bA.mtx.Unlock() defer bA.mtx.Unlock()
return bA.copy() return bA.copy()
@ -85,9 +95,11 @@ func (bA *BitArray) copyBits(bits uint) *BitArray {
// Returns a BitArray of larger bits size. // Returns a BitArray of larger bits size.
func (bA *BitArray) Or(o *BitArray) *BitArray { func (bA *BitArray) Or(o *BitArray) *BitArray {
if bA == nil {
o.Copy()
}
bA.mtx.Lock() bA.mtx.Lock()
defer bA.mtx.Unlock() defer bA.mtx.Unlock()
c := bA.copyBits(MaxUint(bA.Bits, o.Bits)) c := bA.copyBits(MaxUint(bA.Bits, o.Bits))
for i := 0; i < len(c.Elems); i++ { for i := 0; i < len(c.Elems); i++ {
c.Elems[i] |= o.Elems[i] c.Elems[i] |= o.Elems[i]
@ -97,9 +109,11 @@ func (bA *BitArray) Or(o *BitArray) *BitArray {
// Returns a BitArray of smaller bit size. // Returns a BitArray of smaller bit size.
func (bA *BitArray) And(o *BitArray) *BitArray { func (bA *BitArray) And(o *BitArray) *BitArray {
if bA == nil {
return nil
}
bA.mtx.Lock() bA.mtx.Lock()
defer bA.mtx.Unlock() defer bA.mtx.Unlock()
return bA.and(o) return bA.and(o)
} }
@ -112,9 +126,11 @@ func (bA *BitArray) and(o *BitArray) *BitArray {
} }
func (bA *BitArray) Not() *BitArray { func (bA *BitArray) Not() *BitArray {
if bA == nil {
return nil // Degenerate
}
bA.mtx.Lock() bA.mtx.Lock()
defer bA.mtx.Unlock() defer bA.mtx.Unlock()
c := bA.copy() c := bA.copy()
for i := 0; i < len(c.Elems); i++ { for i := 0; i < len(c.Elems); i++ {
c.Elems[i] = ^c.Elems[i] c.Elems[i] = ^c.Elems[i]
@ -123,9 +139,11 @@ func (bA *BitArray) Not() *BitArray {
} }
func (bA *BitArray) Sub(o *BitArray) *BitArray { func (bA *BitArray) Sub(o *BitArray) *BitArray {
if bA == nil {
return nil
}
bA.mtx.Lock() bA.mtx.Lock()
defer bA.mtx.Unlock() defer bA.mtx.Unlock()
if bA.Bits > o.Bits { if bA.Bits > o.Bits {
c := bA.copy() c := bA.copy()
for i := 0; i < len(o.Elems)-1; i++ { for i := 0; i < len(o.Elems)-1; i++ {
@ -139,11 +157,14 @@ func (bA *BitArray) Sub(o *BitArray) *BitArray {
} }
return c return c
} else { } else {
return bA.and(o.Not()) return bA.and(o.Not()) // Note degenerate case where o == nil
} }
} }
func (bA *BitArray) IsFull() bool { func (bA *BitArray) IsFull() bool {
if bA == nil {
return true
}
bA.mtx.Lock() bA.mtx.Lock()
defer bA.mtx.Unlock() defer bA.mtx.Unlock()
@ -165,6 +186,9 @@ func (bA *BitArray) IsFull() bool {
} }
func (bA *BitArray) PickRandom() (uint, bool) { func (bA *BitArray) PickRandom() (uint, bool) {
if bA == nil {
return 0, false
}
bA.mtx.Lock() bA.mtx.Lock()
defer bA.mtx.Unlock() defer bA.mtx.Unlock()
@ -210,14 +234,16 @@ func (bA *BitArray) String() string {
} }
bA.mtx.Lock() bA.mtx.Lock()
defer bA.mtx.Unlock() defer bA.mtx.Unlock()
return bA.stringIndented("") return bA.stringIndented("")
} }
func (bA *BitArray) StringIndented(indent string) string { func (bA *BitArray) StringIndented(indent string) string {
if bA == nil {
return "nil-BitArray"
}
bA.mtx.Lock() bA.mtx.Lock()
defer bA.mtx.Unlock() defer bA.mtx.Unlock()
return bA.StringIndented(indent) return bA.stringIndented(indent)
} }
func (bA *BitArray) stringIndented(indent string) string { func (bA *BitArray) stringIndented(indent string) string {

View File

@ -433,7 +433,6 @@ OUTER_LOOP:
return return
} }
rs := conR.conS.GetRoundState() rs := conR.conS.GetRoundState()
ps.EnsureVoteBitArrays(rs.Height, rs.Validators.Size())
prs := ps.GetRoundState() prs := ps.GetRoundState()
switch sleeping { switch sleeping {
@ -443,7 +442,14 @@ OUTER_LOOP:
sleeping = 0 sleeping = 0
} }
trySendVote := func(voteSet *VoteSet, peerVoteSet *BitArray) (sent bool) { // Returns true when useful work was done.
trySendVote := func(height uint, voteSet *VoteSet, peerVoteSet *BitArray) (sent bool) {
if voteSet == nil {
return false
} else if peerVoteSet == nil {
ps.EnsureVoteBitArrays(height, voteSet.Size())
return true
}
// TODO: give priority to our vote. // TODO: give priority to our vote.
if index, ok := voteSet.BitArray().Sub(peerVoteSet.Copy()).PickRandom(); ok { if index, ok := voteSet.BitArray().Sub(peerVoteSet.Copy()).PickRandom(); ok {
vote := voteSet.GetByIndex(index) vote := voteSet.GetByIndex(index)
@ -456,7 +462,14 @@ OUTER_LOOP:
return false return false
} }
// Returns true when useful work was done.
trySendCommitFromValidation := func(blockMeta *types.BlockMeta, validation *types.Validation, peerVoteSet *BitArray) (sent bool) { trySendCommitFromValidation := func(blockMeta *types.BlockMeta, validation *types.Validation, peerVoteSet *BitArray) (sent bool) {
if validation == nil {
return false
} else if peerVoteSet == nil {
ps.EnsureVoteBitArrays(blockMeta.Header.Height, uint(len(validation.Commits)))
return true
}
if index, ok := validation.BitArray().Sub(prs.Commits.Copy()).PickRandom(); ok { if index, ok := validation.BitArray().Sub(prs.Commits.Copy()).PickRandom(); ok {
commit := validation.Commits[index] commit := validation.Commits[index]
log.Debug("Picked commit to send", "index", index, "commit", commit) log.Debug("Picked commit to send", "index", index, "commit", commit)
@ -482,31 +495,27 @@ OUTER_LOOP:
// If there are lastcommits to send... // If there are lastcommits to send...
if prs.Round == 0 && prs.Step == RoundStepNewHeight { if prs.Round == 0 && prs.Step == RoundStepNewHeight {
if prs.LastCommits != nil && rs.LastCommits != nil { if trySendVote(rs.Height-1, rs.LastCommits, prs.LastCommits) {
if prs.LastCommits.Size() == rs.LastCommits.Size() {
if trySendVote(rs.LastCommits, prs.LastCommits) {
continue OUTER_LOOP continue OUTER_LOOP
} }
} }
}
}
// If there are prevotes to send... // If there are prevotes to send...
if rs.Round == prs.Round && prs.Step <= RoundStepPrevote { if rs.Round == prs.Round && prs.Step <= RoundStepPrevote {
if trySendVote(rs.Prevotes, prs.Prevotes) { if trySendVote(rs.Height, rs.Prevotes, prs.Prevotes) {
continue OUTER_LOOP continue OUTER_LOOP
} }
} }
// If there are precommits to send... // If there are precommits to send...
if rs.Round == prs.Round && prs.Step <= RoundStepPrecommit { if rs.Round == prs.Round && prs.Step <= RoundStepPrecommit {
if trySendVote(rs.Precommits, prs.Precommits) { if trySendVote(rs.Height, rs.Precommits, prs.Precommits) {
continue OUTER_LOOP continue OUTER_LOOP
} }
} }
// If there are any commits to send... // If there are any commits to send...
if trySendVote(rs.Commits, prs.Commits) { if trySendVote(rs.Height, rs.Commits, prs.Commits) {
continue OUTER_LOOP continue OUTER_LOOP
} }
} }
@ -517,7 +526,7 @@ OUTER_LOOP:
// If peer is lagging by height 1, match our LastCommits or SeenValidation to peer's Commits. // If peer is lagging by height 1, match our LastCommits or SeenValidation to peer's Commits.
if rs.Height == prs.Height+1 && rs.LastCommits.Size() > 0 { if rs.Height == prs.Height+1 && rs.LastCommits.Size() > 0 {
// If there are lastcommits to send... // If there are lastcommits to send...
if trySendVote(rs.LastCommits, prs.Commits) { if trySendVote(prs.Height, rs.LastCommits, prs.Commits) {
continue OUTER_LOOP continue OUTER_LOOP
} else { } else {
ps.SetHasAllCatchupCommits(prs.Height) ps.SetHasAllCatchupCommits(prs.Height)
@ -665,10 +674,7 @@ func (ps *PeerState) EnsureVoteBitArrays(height uint, numValidators uint) {
ps.mtx.Lock() ps.mtx.Lock()
defer ps.mtx.Unlock() defer ps.mtx.Unlock()
if ps.Height != height { if ps.Height == height {
return
}
if ps.Prevotes == nil { if ps.Prevotes == nil {
ps.Prevotes = NewBitArray(numValidators) ps.Prevotes = NewBitArray(numValidators)
} }
@ -678,6 +684,11 @@ func (ps *PeerState) EnsureVoteBitArrays(height uint, numValidators uint) {
if ps.Commits == nil { if ps.Commits == nil {
ps.Commits = NewBitArray(numValidators) ps.Commits = NewBitArray(numValidators)
} }
} else if ps.Height == height+1 {
if ps.LastCommits == nil {
ps.LastCommits = NewBitArray(numValidators)
}
}
} }
func (ps *PeerState) SetHasVote(vote *types.Vote, index uint) { func (ps *PeerState) SetHasVote(vote *types.Vote, index uint) {