mirror of
https://github.com/Llewellynvdm/fzf.git
synced 2024-12-23 11:29:01 +00:00
Fix race conditions
- Wait for completions of goroutines when cancelling a search - Remove shared access to rank field of Item
This commit is contained in:
parent
1db68a3976
commit
9dbf6b02d2
@ -64,7 +64,10 @@ func Run(options *Options) {
|
||||
var chunkList *ChunkList
|
||||
if len(opts.WithNth) == 0 {
|
||||
chunkList = NewChunkList(func(data *string, index int) *Item {
|
||||
return &Item{text: data, rank: Rank{0, 0, uint32(index)}}
|
||||
return &Item{
|
||||
text: data,
|
||||
index: uint32(index),
|
||||
rank: Rank{0, 0, uint32(index)}}
|
||||
})
|
||||
} else {
|
||||
chunkList = NewChunkList(func(data *string, index int) *Item {
|
||||
@ -72,6 +75,7 @@ func Run(options *Options) {
|
||||
item := Item{
|
||||
text: Transform(tokens, opts.WithNth).whole,
|
||||
origText: data,
|
||||
index: uint32(index),
|
||||
rank: Rank{0, 0, uint32(index)}}
|
||||
return &item
|
||||
})
|
||||
|
22
src/item.go
22
src/item.go
@ -1,9 +1,6 @@
|
||||
package fzf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
import "fmt"
|
||||
|
||||
type Offset [2]int32
|
||||
|
||||
@ -11,6 +8,7 @@ type Item struct {
|
||||
text *string
|
||||
origText *string
|
||||
transformed *Transformed
|
||||
index uint32
|
||||
offsets []Offset
|
||||
rank Rank
|
||||
}
|
||||
@ -21,11 +19,10 @@ type Rank struct {
|
||||
index uint32
|
||||
}
|
||||
|
||||
func (i *Item) Rank() Rank {
|
||||
if i.rank.matchlen > 0 || i.rank.strlen > 0 {
|
||||
func (i *Item) Rank(cache bool) Rank {
|
||||
if cache && (i.rank.matchlen > 0 || i.rank.strlen > 0) {
|
||||
return i.rank
|
||||
}
|
||||
sort.Sort(ByOrder(i.offsets))
|
||||
matchlen := 0
|
||||
prevEnd := 0
|
||||
for _, offset := range i.offsets {
|
||||
@ -41,8 +38,11 @@ func (i *Item) Rank() Rank {
|
||||
matchlen += end - begin
|
||||
}
|
||||
}
|
||||
i.rank = Rank{uint16(matchlen), uint16(len(*i.text)), i.rank.index}
|
||||
return i.rank
|
||||
rank := Rank{uint16(matchlen), uint16(len(*i.text)), i.index}
|
||||
if cache {
|
||||
i.rank = rank
|
||||
}
|
||||
return rank
|
||||
}
|
||||
|
||||
func (i *Item) Print() {
|
||||
@ -80,8 +80,8 @@ func (a ByRelevance) Swap(i, j int) {
|
||||
}
|
||||
|
||||
func (a ByRelevance) Less(i, j int) bool {
|
||||
irank := a[i].Rank()
|
||||
jrank := a[j].Rank()
|
||||
irank := a[i].Rank(true)
|
||||
jrank := a[j].Rank(true)
|
||||
|
||||
return compareRanks(irank, jrank)
|
||||
}
|
||||
|
@ -31,13 +31,13 @@ func TestRankComparison(t *testing.T) {
|
||||
// Match length, string length, index
|
||||
func TestItemRank(t *testing.T) {
|
||||
strs := []string{"foo", "foobar", "bar", "baz"}
|
||||
item1 := Item{text: &strs[0], rank: Rank{0, 0, 1}, offsets: []Offset{}}
|
||||
rank1 := item1.Rank()
|
||||
item1 := Item{text: &strs[0], index: 1, offsets: []Offset{}}
|
||||
rank1 := item1.Rank(true)
|
||||
if rank1.matchlen != 0 || rank1.strlen != 3 || rank1.index != 1 {
|
||||
t.Error(item1.Rank())
|
||||
t.Error(item1.Rank(true))
|
||||
}
|
||||
// Only differ in index
|
||||
item2 := Item{text: &strs[0], rank: Rank{0, 0, 0}, offsets: []Offset{}}
|
||||
item2 := Item{text: &strs[0], index: 0, offsets: []Offset{}}
|
||||
|
||||
items := []*Item{&item1, &item2}
|
||||
sort.Sort(ByRelevance(items))
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -134,10 +135,13 @@ func (m *Matcher) scan(request MatchRequest, limit int) (*Merger, bool) {
|
||||
slices := m.sliceChunks(request.chunks)
|
||||
numSlices := len(slices)
|
||||
resultChan := make(chan partialResult, numSlices)
|
||||
countChan := make(chan int, numSlices)
|
||||
countChan := make(chan int, numChunks)
|
||||
waitGroup := sync.WaitGroup{}
|
||||
|
||||
for idx, chunks := range slices {
|
||||
waitGroup.Add(1)
|
||||
go func(idx int, chunks []*Chunk) {
|
||||
defer func() { waitGroup.Done() }()
|
||||
sliceMatches := []*Item{}
|
||||
for _, chunk := range chunks {
|
||||
var matches []*Item
|
||||
@ -159,6 +163,12 @@ func (m *Matcher) scan(request MatchRequest, limit int) (*Merger, bool) {
|
||||
}(idx, chunks)
|
||||
}
|
||||
|
||||
wait := func() bool {
|
||||
cancelled.Set(true)
|
||||
waitGroup.Wait()
|
||||
return true
|
||||
}
|
||||
|
||||
count := 0
|
||||
matchCount := 0
|
||||
for matchesInChunk := range countChan {
|
||||
@ -166,7 +176,7 @@ func (m *Matcher) scan(request MatchRequest, limit int) (*Merger, bool) {
|
||||
matchCount += matchesInChunk
|
||||
|
||||
if limit > 0 && matchCount > limit {
|
||||
return nil, true // For --select-1 and --exit-0
|
||||
return nil, wait() // For --select-1 and --exit-0
|
||||
}
|
||||
|
||||
if count == numChunks {
|
||||
@ -174,8 +184,7 @@ func (m *Matcher) scan(request MatchRequest, limit int) (*Merger, bool) {
|
||||
}
|
||||
|
||||
if !empty && m.reqBox.Peak(REQ_RESET) {
|
||||
cancelled.Set(true)
|
||||
return nil, true
|
||||
return nil, wait()
|
||||
}
|
||||
|
||||
if time.Now().Sub(startedAt) > PROGRESS_MIN_DURATION {
|
||||
|
@ -57,7 +57,7 @@ func (mg *Merger) mergedGet(idx int) *Item {
|
||||
continue
|
||||
}
|
||||
if cursor >= 0 {
|
||||
rank := list[cursor].Rank()
|
||||
rank := list[cursor].Rank(false)
|
||||
if minIdx < 0 || compareRanks(rank, minRank) {
|
||||
minRank = rank
|
||||
minIdx = listIdx
|
||||
|
@ -1,6 +1,7 @@
|
||||
package fzf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sort"
|
||||
"testing"
|
||||
@ -13,8 +14,17 @@ func assert(t *testing.T, cond bool, msg ...string) {
|
||||
}
|
||||
|
||||
func randItem() *Item {
|
||||
str := fmt.Sprintf("%d", rand.Uint32())
|
||||
offsets := make([]Offset, rand.Int()%3)
|
||||
for idx := range offsets {
|
||||
sidx := int32(rand.Uint32() % 20)
|
||||
eidx := sidx + int32(rand.Uint32()%20)
|
||||
offsets[idx] = Offset{sidx, eidx}
|
||||
}
|
||||
return &Item{
|
||||
rank: Rank{uint16(rand.Uint32()), uint16(rand.Uint32()), rand.Uint32()}}
|
||||
text: &str,
|
||||
index: rand.Uint32(),
|
||||
offsets: offsets}
|
||||
}
|
||||
|
||||
func TestEmptyMerger(t *testing.T) {
|
||||
|
@ -2,6 +2,7 @@ package fzf
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@ -225,12 +226,14 @@ Loop:
|
||||
}
|
||||
|
||||
func dupItem(item *Item, offsets []Offset) *Item {
|
||||
sort.Sort(ByOrder(offsets))
|
||||
return &Item{
|
||||
text: item.text,
|
||||
origText: item.origText,
|
||||
transformed: item.transformed,
|
||||
index: item.index,
|
||||
offsets: offsets,
|
||||
rank: Rank{0, 0, item.rank.index}}
|
||||
rank: Rank{0, 0, item.index}}
|
||||
}
|
||||
|
||||
func (p *Pattern) fuzzyMatch(chunk *Chunk) []*Item {
|
||||
|
Loading…
Reference in New Issue
Block a user