Skip to content

Commit 1b05ba2

Browse files
Fix crash caused by simultaneous read/write of scanner buffer (#2813)
2 parents 6f13b42 + 28474b0 commit 1b05ba2

File tree

1 file changed

+15
-4
lines changed

1 file changed

+15
-4
lines changed

pkg/tasks/tasks.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,18 +164,27 @@ func (self *ViewBufferManager) NewCmdTask(start func() (*exec.Cmd, io.Reader), p
164164
scanner := bufio.NewScanner(r)
165165
scanner.Split(bufio.ScanLines)
166166

167-
data := make(chan []byte)
167+
lineChan := make(chan []byte)
168+
lineWrittenChan := make(chan struct{})
168169

169170
// We're reading from the scanner in a separate goroutine because on windows
170171
// if running git through a shim, we sometimes kill the parent process without
171172
// killing its children, meaning the scanner blocks forever. This solution
172173
// leaves us with a dead goroutine, but it's better than blocking all
173174
// rendering to main views.
174175
go utils.Safe(func() {
176+
defer close(lineChan)
175177
for scanner.Scan() {
176-
data <- scanner.Bytes()
178+
select {
179+
case <-opts.Stop:
180+
return
181+
case lineChan <- scanner.Bytes():
182+
// We need to confirm the data has been fed into the view before we
183+
// pull more from the scanner because the scanner uses the same backing
184+
// array and we don't want to be mutating that while it's being written
185+
<-lineWrittenChan
186+
}
177187
}
178-
close(data)
179188
})
180189

181190
loaded := false
@@ -222,7 +231,7 @@ func (self *ViewBufferManager) NewCmdTask(start func() (*exec.Cmd, io.Reader), p
222231
select {
223232
case <-opts.Stop:
224233
break outer
225-
case line, ok = <-data:
234+
case line, ok = <-lineChan:
226235
break
227236
}
228237

@@ -243,6 +252,7 @@ func (self *ViewBufferManager) NewCmdTask(start func() (*exec.Cmd, io.Reader), p
243252
break outer
244253
}
245254
writeToView(append(line, '\n'))
255+
lineWrittenChan <- struct{}{}
246256

247257
if i+1 == linesToRead.InitialRefreshAfter {
248258
// 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
269279
onDone()
270280

271281
close(done)
282+
close(lineWrittenChan)
272283
})
273284

274285
self.readLines <- linesToRead

0 commit comments

Comments
 (0)