mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-09-10 07:36:27 -04:00
Serialize concurrent writes to the streamed command's output writer
runAndStreamAux funnels a command's stdout and stderr into a single cmdWriter (the command-log panel, or a buffer when output is suppressed) from two separate goroutines: stderr through the MultiWriter set on cmd.Stderr, and stdout through the onRun callback. Those goroutines wrote the shared writer without any synchronization, racing on the prefixWriter's prefixWritten flag and interleaving the two streams. Wrap the writer so its writes are serialized. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4b97c2ba61
commit
d097519c05
|
|
@ -244,6 +244,10 @@ func (self *cmdObjRunner) runAndStreamAux(
|
|||
} else {
|
||||
cmdWriter = self.guiIO.newCmdWriterFn()
|
||||
}
|
||||
// The command's stdout and stderr are streamed to cmdWriter concurrently
|
||||
// from separate goroutines (stderr via the MultiWriter below, stdout via
|
||||
// onRun), so it must be safe for concurrent writes.
|
||||
cmdWriter = &synchronizedWriter{writer: cmdWriter}
|
||||
|
||||
if cmdObj.ShouldLog() {
|
||||
self.logCmdObj(cmdObj)
|
||||
|
|
@ -451,6 +455,20 @@ func (self *cmdObjRunner) getCheckForCredentialRequestFunc() func([]byte) (Crede
|
|||
}
|
||||
}
|
||||
|
||||
// synchronizedWriter serializes writes to its underlying writer so that it can
|
||||
// be written from multiple goroutines at once (see runAndStreamAux, which
|
||||
// streams a command's stdout and stderr to one writer from two goroutines).
|
||||
type synchronizedWriter struct {
|
||||
mutex deadlock.Mutex
|
||||
writer io.Writer
|
||||
}
|
||||
|
||||
func (self *synchronizedWriter) Write(p []byte) (int, error) {
|
||||
self.mutex.Lock()
|
||||
defer self.mutex.Unlock()
|
||||
return self.writer.Write(p)
|
||||
}
|
||||
|
||||
type Buffer struct {
|
||||
b bytes.Buffer
|
||||
m deadlock.Mutex
|
||||
|
|
|
|||
Loading…
Reference in a new issue