Remove possible races (#4070)

This commit is contained in:
Junegunn Choi 2024-11-03 15:14:35 +09:00
parent bacc8609ee
commit 19495eb9bb
No known key found for this signature in database
GPG Key ID: 254BC280FEF9C627
3 changed files with 39 additions and 39 deletions

View File

@ -172,7 +172,9 @@ func Run(opts *Options) (int, error) {
return chunkList.Push(data) return chunkList.Push(data)
}, eventBox, executor, opts.ReadZero, opts.Filter == nil) }, eventBox, executor, opts.ReadZero, opts.Filter == nil)
go reader.ReadSource(opts.Input, opts.WalkerRoot, opts.WalkerOpts, opts.WalkerSkip, initialReload, initialEnv) readyChan := make(chan bool)
go reader.ReadSource(opts.Input, opts.WalkerRoot, opts.WalkerOpts, opts.WalkerSkip, initialReload, initialEnv, readyChan)
<-readyChan
} }
// Matcher // Matcher
@ -224,7 +226,7 @@ func Run(opts *Options) (int, error) {
} }
return false return false
}, eventBox, executor, opts.ReadZero, false) }, eventBox, executor, opts.ReadZero, false)
reader.ReadSource(opts.Input, opts.WalkerRoot, opts.WalkerOpts, opts.WalkerSkip, initialReload, initialEnv) reader.ReadSource(opts.Input, opts.WalkerRoot, opts.WalkerOpts, opts.WalkerSkip, initialReload, initialEnv, nil)
} else { } else {
eventBox.Unwatch(EvtReadNew) eventBox.Unwatch(EvtReadNew)
eventBox.WaitFor(EvtReadFin) eventBox.WaitFor(EvtReadFin)
@ -299,7 +301,9 @@ func Run(opts *Options) (int, error) {
itemIndex = 0 itemIndex = 0
inputRevision.bumpMajor() inputRevision.bumpMajor()
header = make([]string, 0, opts.HeaderLines) header = make([]string, 0, opts.HeaderLines)
reader.restart(command, environ) readyChan := make(chan bool)
go reader.restart(command, environ, readyChan)
<-readyChan
} }
exitCode := ExitOk exitCode := ExitOk

View File

@ -6,7 +6,6 @@ import (
"io" "io"
"io/fs" "io/fs"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"sync" "sync"
"sync/atomic" "sync/atomic"
@ -98,20 +97,14 @@ func (r *Reader) terminate() {
r.mutex.Unlock() r.mutex.Unlock()
} }
func (r *Reader) restart(command commandSpec, environ []string) { func (r *Reader) restart(command commandSpec, environ []string, readyChan chan bool) {
r.event = int32(EvtReady) r.event = int32(EvtReady)
r.startEventPoller() r.startEventPoller()
success := r.readFromCommand(command.command, environ, func() {
r.mutex.Lock() readyChan <- true
defer r.mutex.Unlock() })
r.fin(success)
if exec, execOut := r.startCommand(command.command, environ); exec != nil { removeFiles(command.tempFiles)
go func() {
success := r.feedCommandOutput(exec, execOut)
r.fin(success)
removeFiles(command.tempFiles)
}()
}
} }
func (r *Reader) readChannel(inputChan chan string) bool { func (r *Reader) readChannel(inputChan chan string) bool {
@ -128,21 +121,29 @@ func (r *Reader) readChannel(inputChan chan string) bool {
} }
// ReadSource reads data from the default command or from standard input // ReadSource reads data from the default command or from standard input
func (r *Reader) ReadSource(inputChan chan string, root string, opts walkerOpts, ignores []string, initCmd string, initEnv []string) { func (r *Reader) ReadSource(inputChan chan string, root string, opts walkerOpts, ignores []string, initCmd string, initEnv []string, readyChan chan bool) {
r.startEventPoller() r.startEventPoller()
var success bool var success bool
signalReady := func() {
if readyChan != nil {
readyChan <- true
}
}
if inputChan != nil { if inputChan != nil {
signalReady()
success = r.readChannel(inputChan) success = r.readChannel(inputChan)
} else if len(initCmd) > 0 { } else if len(initCmd) > 0 {
success = r.readFromCommand(initCmd, initEnv) success = r.readFromCommand(initCmd, initEnv, signalReady)
} else if util.IsTty(os.Stdin) { } else if util.IsTty(os.Stdin) {
cmd := os.Getenv("FZF_DEFAULT_COMMAND") cmd := os.Getenv("FZF_DEFAULT_COMMAND")
if len(cmd) == 0 { if len(cmd) == 0 {
signalReady()
success = r.readFiles(root, opts, ignores) success = r.readFiles(root, opts, ignores)
} else { } else {
success = r.readFromCommand(cmd, initEnv) success = r.readFromCommand(cmd, initEnv, signalReady)
} }
} else { } else {
signalReady()
success = r.readFromStdin() success = r.readFromStdin()
} }
r.fin(success) r.fin(success)
@ -304,8 +305,9 @@ func (r *Reader) readFiles(root string, opts walkerOpts, ignores []string) bool
return fastwalk.Walk(&conf, root, fn) == nil return fastwalk.Walk(&conf, root, fn) == nil
} }
// Should be called with the mutex held func (r *Reader) readFromCommand(command string, environ []string, signalReady func()) bool {
func (r *Reader) startCommand(command string, environ []string) (*exec.Cmd, io.ReadCloser) { r.mutex.Lock()
r.termFunc = nil r.termFunc = nil
r.command = &command r.command = &command
exec := r.executor.ExecCommand(command, true) exec := r.executor.ExecCommand(command, true)
@ -314,7 +316,8 @@ func (r *Reader) startCommand(command string, environ []string) (*exec.Cmd, io.R
} }
execOut, err := exec.StdoutPipe() execOut, err := exec.StdoutPipe()
if err != nil || exec.Start() != nil { if err != nil || exec.Start() != nil {
return nil, nil r.mutex.Unlock()
return false
} }
// Function to call to terminate the running command // Function to call to terminate the running command
@ -323,20 +326,9 @@ func (r *Reader) startCommand(command string, environ []string) (*exec.Cmd, io.R
util.KillCommand(exec) util.KillCommand(exec)
} }
return exec, execOut signalReady()
} r.mutex.Unlock()
func (r *Reader) feedCommandOutput(exec *exec.Cmd, execOut io.ReadCloser) bool {
r.feed(execOut) r.feed(execOut)
return exec.Wait() == nil return exec.Wait() == nil
} }
func (r *Reader) readFromCommand(command string, environ []string) bool {
r.mutex.Lock()
exec, execOut := r.startCommand(command, environ)
r.mutex.Unlock()
if exec == nil {
return false
}
return r.feedCommandOutput(exec, execOut)
}

View File

@ -23,8 +23,12 @@ func TestReadFromCommand(t *testing.T) {
} }
// Normal command // Normal command
reader.fin(reader.readFromCommand(`echo abc&&echo def`, nil)) counter := 0
if len(strs) != 2 || strs[0] != "abc" || strs[1] != "def" { ready := func() {
counter++
}
reader.fin(reader.readFromCommand(`echo abc&&echo def`, nil, ready))
if len(strs) != 2 || strs[0] != "abc" || strs[1] != "def" || counter != 1 {
t.Errorf("%s", strs) t.Errorf("%s", strs)
} }
@ -48,9 +52,9 @@ func TestReadFromCommand(t *testing.T) {
reader.startEventPoller() reader.startEventPoller()
// Failing command // Failing command
reader.fin(reader.readFromCommand(`no-such-command`, nil)) reader.fin(reader.readFromCommand(`no-such-command`, nil, ready))
strs = []string{} strs = []string{}
if len(strs) > 0 { if len(strs) > 0 || counter != 2 {
t.Errorf("%s", strs) t.Errorf("%s", strs)
} }