summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Duffield <jessedduffield@gmail.com>2023-07-23 13:55:51 +1000
committerGitHub <noreply@github.com>2023-07-23 13:55:51 +1000
commit1b05ba252c7e5ff2725b41ca2643c61b9ed84979 (patch)
tree83751e1811b1c147f99a958cbc3d09ad554f0144
parent6f13b422799297d5f167ed3a24590f5e44eced0c (diff)
parent28474b08eeda872d67227ebc09f8a873e2bf2aad (diff)
Fix crash caused by simultaneous read/write of scanner buffer (#2813)v0.39.3
-rw-r--r--pkg/tasks/tasks.go19
1 files changed, 15 insertions, 4 deletions
diff --git a/pkg/tasks/tasks.go b/pkg/tasks/tasks.go
index 8fcebf4b4..88ce3cfce 100644
--- a/pkg/tasks/tasks.go
+++ b/pkg/tasks/tasks.go
@@ -164,7 +164,8 @@ func (self *ViewBufferManager) NewCmdTask(start func() (*exec.Cmd, io.Reader), p
scanner := bufio.NewScanner(r)
scanner.Split(bufio.ScanLines)
- data := make(chan []byte)
+ lineChan := make(chan []byte)
+ lineWrittenChan := make(chan struct{})
// We're reading from the scanner in a separate goroutine because on windows
// if running git through a shim, we sometimes kill the parent process without
@@ -172,10 +173,18 @@ func (self *ViewBufferManager) NewCmdTask(start func() (*exec.Cmd, io.Reader), p
// leaves us with a dead goroutine, but it's better than blocking all
// rendering to main views.
go utils.Safe(func() {
+ defer close(lineChan)
for scanner.Scan() {
- data <- scanner.Bytes()
+ select {
+ case <-opts.Stop:
+ return
+ case lineChan <- scanner.Bytes():
+ // We need to confirm the data has been fed into the view before we
+ // pull more from the scanner because the scanner uses the same backing
+ // array and we don't want to be mutating that while it's being written
+ <-lineWrittenChan
+ }
}
- close(data)
})
loaded := false
@@ -222,7 +231,7 @@ func (self *ViewBufferManager) NewCmdTask(start func() (*exec.Cmd, io.Reader), p
select {
case <-opts.Stop:
break outer
- case line, ok = <-data:
+ case line, ok = <-lineChan:
break
}
@@ -243,6 +252,7 @@ func (self *ViewBufferManager) NewCmdTask(start func() (*exec.Cmd, io.Reader), p
break outer
}
writeToView(append(line, '\n'))
+ lineWrittenChan <- struct{}{}
if i+1 == linesToRead.InitialRefreshAfter {
// We have read enough lines to fill the view, so do a first refresh
@@ -269,6 +279,7 @@ func (self *ViewBufferManager) NewCmdTask(start func() (*exec.Cmd, io.Reader), p
onDone()
close(done)
+ close(lineWrittenChan)
})
self.readLines <- linesToRead