summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJunegunn Choi <junegunn.c@gmail.com>2025-06-21 23:24:38 +0900
committerJunegunn Choi <junegunn.c@gmail.com>2025-06-21 23:24:38 +0900
commit247d168af6fabcc322f54ad366f4fb45f781137b (patch)
tree75faf7646a4690a7b6868ae9a561d13c80ae0149 /src
parentb2a8a283c79f30f5027fb181bc27668ffc37d8b9 (diff)
downloadfzf-247d168af6fabcc322f54ad366f4fb45f781137b.tar.gz
Terminate running background transform on exit
Close #4422
Diffstat (limited to 'src')
-rw-r--r--src/terminal.go8
-rw-r--r--src/util/concurrent_set.go39
2 files changed, 47 insertions, 0 deletions
diff --git a/src/terminal.go b/src/terminal.go
index 16c187b8..bca98530 100644
--- a/src/terminal.go
+++ b/src/terminal.go
@@ -9,6 +9,7 @@ import (
"math"
"net"
"os"
+ "os/exec"
"os/signal"
"regexp"
"sort"
@@ -377,6 +378,7 @@ type Terminal struct {
version int64
revision revision
bgVersion int64
+ runningCmds *util.ConcurrentSet[*exec.Cmd]
reqBox *util.EventBox
initialPreviewOpts previewOpts
previewOpts previewOpts
@@ -1030,6 +1032,7 @@ func NewTerminal(opts *Options, eventBox *util.EventBox, executor *util.Executor
proxyScript: opts.ProxyScript,
merger: EmptyMerger(revision{}),
selected: make(map[int32]selectedItem),
+ runningCmds: util.NewConcurrentSet[*exec.Cmd](),
reqBox: util.NewEventBox(),
initialPreviewOpts: opts.Preview,
previewOpts: opts.Preview,
@@ -4377,6 +4380,7 @@ func (t *Terminal) captureAsync(a action, firstLineOnly bool, callback func(stri
reader := bufio.NewReader(out)
var output string
if err := cmd.Start(); err == nil {
+ t.runningCmds.Add(cmd)
if firstLineOnly {
output, _ = reader.ReadString('\n')
output = strings.TrimRight(output, "\r\n")
@@ -4385,6 +4389,7 @@ func (t *Terminal) captureAsync(a action, firstLineOnly bool, callback func(stri
output = string(bytes)
}
cmd.Wait()
+ t.runningCmds.Remove(cmd)
}
t.callbackChan <- versionedCallback{version, func() { callback(output) }}
}
@@ -5053,6 +5058,9 @@ func (t *Terminal) Loop() error {
if code <= ExitNoMatch && t.history != nil {
t.history.append(string(t.input))
}
+ t.runningCmds.ForEach(func(cmd *exec.Cmd) {
+ util.KillCommand(cmd)
+ })
running = false
t.mutex.Unlock()
}
diff --git a/src/util/concurrent_set.go b/src/util/concurrent_set.go
new file mode 100644
index 00000000..c2ffc619
--- /dev/null
+++ b/src/util/concurrent_set.go
@@ -0,0 +1,39 @@
+package util
+
+import "sync"
+
+// ConcurrentSet is a thread-safe set implementation.
+type ConcurrentSet[T comparable] struct {
+ lock sync.RWMutex
+ items map[T]struct{}
+}
+
+// NewConcurrentSet creates a new ConcurrentSet.
+func NewConcurrentSet[T comparable]() *ConcurrentSet[T] {
+ return &ConcurrentSet[T]{
+ items: make(map[T]struct{}),
+ }
+}
+
+// Add adds an item to the set.
+func (s *ConcurrentSet[T]) Add(item T) {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+ s.items[item] = struct{}{}
+}
+
+// Remove removes an item from the set.
+func (s *ConcurrentSet[T]) Remove(item T) {
+ s.lock.Lock()
+ defer s.lock.Unlock()
+ delete(s.items, item)
+}
+
+// ForEach iterates over each item in the set and applies the provided function.
+func (s *ConcurrentSet[T]) ForEach(fn func(item T)) {
+ s.lock.RLock()
+ defer s.lock.RUnlock()
+ for item := range s.items {
+ fn(item)
+ }
+}