| 118 | } |
| 119 | |
| 120 | func (self *ViewBufferManager) NewCmdTask(start func() (*exec.Cmd, io.Reader), prefix string, linesToRead LinesToRead, onDoneFn func()) func(TaskOpts) error { |
| 121 | return func(opts TaskOpts) error { |
| 122 | var onDoneOnce sync.Once |
| 123 | var onFirstPageShownOnce sync.Once |
| 124 | |
| 125 | onFirstPageShown := func() { |
| 126 | onFirstPageShownOnce.Do(func() { |
| 127 | opts.InitialContentLoaded() |
| 128 | }) |
| 129 | } |
| 130 | |
| 131 | onDone := func() { |
| 132 | if onDoneFn != nil { |
| 133 | onDoneOnce.Do(onDoneFn) |
| 134 | } |
| 135 | onFirstPageShown() |
| 136 | } |
| 137 | |
| 138 | if self.throttle { |
| 139 | self.Log.Info("throttling task") |
| 140 | time.Sleep(THROTTLE_TIME) |
| 141 | } |
| 142 | |
| 143 | select { |
| 144 | case <-opts.Stop: |
| 145 | onDone() |
| 146 | return nil |
| 147 | default: |
| 148 | } |
| 149 | |
| 150 | startTime := time.Now() |
| 151 | cmd, r := start() |
| 152 | timeToStart := time.Since(startTime) |
| 153 | |
| 154 | done := make(chan struct{}) |
| 155 | |
| 156 | go utils.Safe(func() { |
| 157 | select { |
| 158 | case <-done: |
| 159 | // The command finished and did not have to be preemptively stopped before the next command. |
| 160 | // No need to throttle. |
| 161 | self.throttle = false |
| 162 | case <-opts.Stop: |
| 163 | // we use the time it took to start the program as a way of checking if things |
| 164 | // are running slow at the moment. This is admittedly a crude estimate, but |
| 165 | // the point is that we only want to throttle when things are running slow |
| 166 | // and the user is flicking through a bunch of items. |
| 167 | self.throttle = time.Since(startTime) < THROTTLE_TIME && timeToStart > COMMAND_START_THRESHOLD |
| 168 | |
| 169 | // Kill the still-running command. The only reason to do this is to save CPU usage |
| 170 | // when flicking through several very long diffs when diff.algorithm = histogram is |
| 171 | // being used, in which case multiple git processes continue to calculate expensive |
| 172 | // diffs in the background even though they have been stopped already. |
| 173 | // |
| 174 | // Unfortunately this will do nothing on Windows, so Windows users will have to live |
| 175 | // with the higher CPU usage. |
| 176 | if err := oscommands.TerminateProcessGracefully(cmd); err != nil { |
| 177 | self.Log.Errorf("error when trying to terminate cmd task: %v; Command: %v %v", err, cmd.Path, cmd.Args) |