From 19495eb9bbfa874647e7bc69e0fdff49d68b1dcf Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Sun, 3 Nov 2024 15:14:35 +0900 Subject: [PATCH] Remove possible races (#4070) --- src/core.go | 10 ++++++--- src/reader.go | 56 ++++++++++++++++++++-------------------------- src/reader_test.go | 12 ++++++---- 3 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/core.go b/src/core.go index a452722..89f40ce 100644 --- a/src/core.go +++ b/src/core.go @@ -172,7 +172,9 @@ func Run(opts *Options) (int, error) { return chunkList.Push(data) }, 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 @@ -224,7 +226,7 @@ func Run(opts *Options) (int, error) { } return 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 { eventBox.Unwatch(EvtReadNew) eventBox.WaitFor(EvtReadFin) @@ -299,7 +301,9 @@ func Run(opts *Options) (int, error) { itemIndex = 0 inputRevision.bumpMajor() header = make([]string, 0, opts.HeaderLines) - reader.restart(command, environ) + readyChan := make(chan bool) + go reader.restart(command, environ, readyChan) + <-readyChan } exitCode := ExitOk diff --git a/src/reader.go b/src/reader.go index 50e7809..8178293 100644 --- a/src/reader.go +++ b/src/reader.go @@ -6,7 +6,6 @@ import ( "io" "io/fs" "os" - "os/exec" "path/filepath" "sync" "sync/atomic" @@ -98,20 +97,14 @@ func (r *Reader) terminate() { 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.startEventPoller() - - r.mutex.Lock() - defer r.mutex.Unlock() - - if exec, execOut := r.startCommand(command.command, environ); exec != nil { - go func() { - success := r.feedCommandOutput(exec, execOut) - r.fin(success) - removeFiles(command.tempFiles) - }() - } + success := r.readFromCommand(command.command, environ, func() { + readyChan <- true + }) + r.fin(success) + removeFiles(command.tempFiles) } 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 -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() var success bool + signalReady := func() { + if readyChan != nil { + readyChan <- true + } + } if inputChan != nil { + signalReady() success = r.readChannel(inputChan) } else if len(initCmd) > 0 { - success = r.readFromCommand(initCmd, initEnv) + success = r.readFromCommand(initCmd, initEnv, signalReady) } else if util.IsTty(os.Stdin) { cmd := os.Getenv("FZF_DEFAULT_COMMAND") if len(cmd) == 0 { + signalReady() success = r.readFiles(root, opts, ignores) } else { - success = r.readFromCommand(cmd, initEnv) + success = r.readFromCommand(cmd, initEnv, signalReady) } } else { + signalReady() success = r.readFromStdin() } 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 } -// Should be called with the mutex held -func (r *Reader) startCommand(command string, environ []string) (*exec.Cmd, io.ReadCloser) { +func (r *Reader) readFromCommand(command string, environ []string, signalReady func()) bool { + r.mutex.Lock() + r.termFunc = nil r.command = &command 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() if err != nil || exec.Start() != nil { - return nil, nil + r.mutex.Unlock() + return false } // 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) } - return exec, execOut -} + signalReady() + r.mutex.Unlock() -func (r *Reader) feedCommandOutput(exec *exec.Cmd, execOut io.ReadCloser) bool { r.feed(execOut) 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) -} diff --git a/src/reader_test.go b/src/reader_test.go index 56f9a1b..cab4c01 100644 --- a/src/reader_test.go +++ b/src/reader_test.go @@ -23,8 +23,12 @@ func TestReadFromCommand(t *testing.T) { } // Normal command - reader.fin(reader.readFromCommand(`echo abc&&echo def`, nil)) - if len(strs) != 2 || strs[0] != "abc" || strs[1] != "def" { + counter := 0 + 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) } @@ -48,9 +52,9 @@ func TestReadFromCommand(t *testing.T) { reader.startEventPoller() // Failing command - reader.fin(reader.readFromCommand(`no-such-command`, nil)) + reader.fin(reader.readFromCommand(`no-such-command`, nil, ready)) strs = []string{} - if len(strs) > 0 { + if len(strs) > 0 || counter != 2 { t.Errorf("%s", strs) }