Skip to content

Commit 61f8ef7

Browse files
committed
reader: Fixed race condition where reader would be terminated before it
was properly initialized. Currently, when reloading input very quickly (happens when updating the prompt quickly with 'reload' bound to 'change') it is possible for the reader's terminate method to be called before readFromCommand properly spawns the new reader process. This means the process is never killed and input is not reloaded until the process completes naturally or another 'EvtSearchNew' is triggered. An example where this becomes a problem is when using fzf with rg integration and searching a large directory. Occasionally, the query being processed by rg is not the same as the query displayed by fzf and only after further modifying the query or waiting until rg finishes will the input reload to the correct state. This commit introduces a 'started' condition that the terminate method will wait for before trying to kill the process. The 'started' condition is signalled only when the reader reaches a state where it can be safely terminated.
1 parent ff16877 commit 61f8ef7

File tree

2 files changed

+44
-15
lines changed

2 files changed

+44
-15
lines changed

src/core.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,7 @@ func Run(opts *Options) (int, error) {
299299
itemIndex = 0
300300
inputRevision.bumpMajor()
301301
header = make([]string, 0, opts.HeaderLines)
302+
reader.reset()
302303
go reader.restart(command, environ)
303304
}
304305

src/reader.go

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,27 @@ import (
1818

1919
// Reader reads from command or standard input
2020
type Reader struct {
21-
pusher func([]byte) bool
22-
executor *util.Executor
23-
eventBox *util.EventBox
24-
delimNil bool
25-
event int32
26-
finChan chan bool
27-
mutex sync.Mutex
28-
exec *exec.Cmd
29-
execOut io.ReadCloser
30-
command *string
31-
killed bool
32-
wait bool
21+
pusher func([]byte) bool
22+
executor *util.Executor
23+
eventBox *util.EventBox
24+
delimNil bool
25+
event int32
26+
finChan chan bool
27+
mutex *sync.Mutex
28+
exec *exec.Cmd
29+
execOut io.ReadCloser
30+
command *string
31+
killed bool
32+
started bool
33+
startedCon *sync.Cond
34+
wait bool
3335
}
3436

3537
// NewReader returns new Reader object
3638
func NewReader(pusher func([]byte) bool, eventBox *util.EventBox, executor *util.Executor, delimNil bool, wait bool) *Reader {
37-
return &Reader{pusher, executor, eventBox, delimNil, int32(EvtReady), make(chan bool, 1), sync.Mutex{}, nil, nil, nil, false, wait}
39+
mutex := sync.Mutex{}
40+
startedCon := sync.NewCond(&mutex)
41+
return &Reader{pusher, executor, eventBox, delimNil, int32(EvtReady), make(chan bool, 1), &mutex, nil, nil, nil, false, false, startedCon, wait}
3842
}
3943

4044
func (r *Reader) startEventPoller() {
@@ -79,6 +83,13 @@ func (r *Reader) fin(success bool) {
7983

8084
func (r *Reader) terminate() {
8185
r.mutex.Lock()
86+
if !r.started {
87+
r.startedCon.Wait()
88+
}
89+
if r.killed {
90+
r.mutex.Unlock()
91+
return
92+
}
8293
r.killed = true
8394
if r.exec != nil && r.exec.Process != nil {
8495
r.execOut.Close()
@@ -89,6 +100,20 @@ func (r *Reader) terminate() {
89100
r.mutex.Unlock()
90101
}
91102

103+
func (r *Reader) signalReaderStarted() {
104+
r.mutex.Lock()
105+
r.started = true
106+
r.startedCon.Broadcast()
107+
r.mutex.Unlock()
108+
}
109+
110+
func (r *Reader) reset() {
111+
r.mutex.Lock()
112+
r.killed = false
113+
r.started = false
114+
r.mutex.Unlock()
115+
}
116+
92117
func (r *Reader) restart(command commandSpec, environ []string) {
93118
r.event = int32(EvtReady)
94119
r.startEventPoller()
@@ -98,6 +123,7 @@ func (r *Reader) restart(command commandSpec, environ []string) {
98123
}
99124

100125
func (r *Reader) readChannel(inputChan chan string) bool {
126+
r.signalReaderStarted()
101127
for {
102128
item, more := <-inputChan
103129
if !more {
@@ -220,6 +246,7 @@ func (r *Reader) feed(src io.Reader) {
220246
}
221247

222248
func (r *Reader) readFromStdin() bool {
249+
r.signalReaderStarted()
223250
r.feed(os.Stdin)
224251
return true
225252
}
@@ -249,7 +276,7 @@ func trimPath(path string) string {
249276
}
250277

251278
func (r *Reader) readFiles(root string, opts walkerOpts, ignores []string) bool {
252-
r.killed = false
279+
r.signalReaderStarted()
253280
conf := fastwalk.Config{
254281
Follow: opts.follow,
255282
// Use forward slashes when running a Windows binary under WSL or MSYS
@@ -290,7 +317,8 @@ func (r *Reader) readFiles(root string, opts walkerOpts, ignores []string) bool
290317

291318
func (r *Reader) readFromCommand(command string, environ []string) bool {
292319
r.mutex.Lock()
293-
r.killed = false
320+
r.started = true
321+
r.startedCon.Broadcast()
294322
r.command = &command
295323
r.exec = r.executor.ExecCommand(command, true)
296324
if environ != nil {

0 commit comments

Comments
 (0)