mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-09-10 07:36:27 -04:00
Guard taskKey with the mutex that already guards the task ID
taskKey is written on the goroutine NewTask spawns, under taskIDMutex, but GetTaskKey read it without the lock — and the string renders in tasks_adapter.go call that from the UI thread while a previous task's goroutine may be writing. A Go string is a two-word value, so a torn read can pair one string's pointer with another's length and index out of bounds, not merely return the wrong key. Take the lock in GetTaskKey, and read the field directly at the one call site that already holds it. No test: the failure needs two goroutines to interleave inside a two-word assignment, which nothing can schedule deterministically. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
1868167a7a
commit
01600042cd
|
|
@ -61,9 +61,13 @@ type ViewBufferManager struct {
|
|||
writer io.Writer
|
||||
|
||||
waitingMutex deadlock.Mutex
|
||||
taskIDMutex deadlock.Mutex
|
||||
Log *logrus.Entry
|
||||
newTaskID int
|
||||
// Guards newTaskID and taskKey, which identify the most recently requested
|
||||
// task. Both are written on the goroutine NewTask spawns, and taskKey is
|
||||
// read from the UI thread (GetTaskKey), so neither may be touched without
|
||||
// holding this.
|
||||
taskIDMutex deadlock.Mutex
|
||||
Log *logrus.Entry
|
||||
newTaskID int
|
||||
// The channel by which the currently-running task is told to read more
|
||||
// lines (e.g. as the user scrolls). Held in an atomic because it's swapped
|
||||
// out as tasks come and go while ReadLines/ReadToEnd read it from the UI
|
||||
|
|
@ -115,6 +119,9 @@ type LinesToRead struct {
|
|||
}
|
||||
|
||||
func (self *ViewBufferManager) GetTaskKey() string {
|
||||
self.taskIDMutex.Lock()
|
||||
defer self.taskIDMutex.Unlock()
|
||||
|
||||
return self.taskKey
|
||||
}
|
||||
|
||||
|
|
@ -488,7 +495,9 @@ func (self *ViewBufferManager) NewTask(f func(TaskOpts) error, key string) error
|
|||
return
|
||||
}
|
||||
|
||||
resetOrigin := self.GetTaskKey() != key && self.onNewKey != nil
|
||||
// Read taskKey directly: we already hold the mutex that guards it, and
|
||||
// GetTaskKey would take it again.
|
||||
resetOrigin := self.taskKey != key && self.onNewKey != nil
|
||||
self.taskKey = key
|
||||
|
||||
self.taskIDMutex.Unlock()
|
||||
|
|
|
|||
Loading…
Reference in a new issue