Refactor progress.go
Vladimir Bauer
9 years ago
| 46 | 46 | // WaitGroup for internal rendering sync |
| 47 | 47 | wg *sync.WaitGroup |
| 48 | 48 | |
| 49 | done chan struct{} | |
| 50 | ops chan func(*pConf) | |
| 51 | stopReqCh chan struct{} | |
| 52 | ||
| 53 | // following is used after (*Progress.done) is closed | |
| 54 | conf pConf | |
| 49 | // quit channel to request p.server to quit | |
| 50 | quit chan struct{} | |
| 51 | // done channel is receiveable after p.server has been quit | |
| 52 | done chan struct{} | |
| 53 | ops chan func(*pConf) | |
| 55 | 54 | } |
| 56 | 55 | |
| 57 | 56 | // New creates new Progress instance, which orchestrates bars rendering process. |
| 72 | 71 | } |
| 73 | 72 | |
| 74 | 73 | p := &Progress{ |
| 75 | wg: new(sync.WaitGroup), | |
| 76 | done: make(chan struct{}), | |
| 77 | ops: make(chan func(*pConf)), | |
| 78 | stopReqCh: make(chan struct{}), | |
| 74 | wg: new(sync.WaitGroup), | |
| 75 | done: make(chan struct{}), | |
| 76 | ops: make(chan func(*pConf)), | |
| 77 | quit: make(chan struct{}), | |
| 79 | 78 | } |
| 80 | 79 | go p.server(conf) |
| 81 | 80 | return p |
| 94 | 93 | select { |
| 95 | 94 | case p.ops <- op: |
| 96 | 95 | return <-result |
| 97 | case <-p.done: | |
| 96 | case <-p.quit: | |
| 98 | 97 | return nil |
| 99 | 98 | } |
| 100 | 99 | } |
| 118 | 117 | select { |
| 119 | 118 | case p.ops <- op: |
| 120 | 119 | return <-result |
| 121 | case <-p.done: | |
| 120 | case <-p.quit: | |
| 122 | 121 | return false |
| 123 | 122 | } |
| 124 | 123 | } |
| 132 | 131 | select { |
| 133 | 132 | case p.ops <- op: |
| 134 | 133 | return <-result |
| 135 | case <-p.done: | |
| 134 | case <-p.quit: | |
| 136 | 135 | return 0 |
| 137 | 136 | } |
| 138 | 137 | } |
| 143 | 142 | // cancelation purposes. |
| 144 | 143 | func (p *Progress) Stop() { |
| 145 | 144 | select { |
| 146 | case <-p.done: | |
| 145 | case <-p.quit: | |
| 147 | 146 | return |
| 148 | 147 | default: |
| 149 | 148 | // complete Total unknown bars |
| 154 | 153 | } |
| 155 | 154 | // wait for all bars to quit |
| 156 | 155 | p.wg.Wait() |
| 157 | // stop request | |
| 158 | p.stopReqCh <- struct{}{} | |
| 156 | // request p.server to quit | |
| 157 | p.quitRequest() | |
| 159 | 158 | // wait for p.server to quit |
| 160 | 159 | <-p.done |
| 161 | 160 | } |
| 162 | 161 | } |
| 163 | 162 | |
| 163 | func (p *Progress) quitRequest() { | |
| 164 | select { | |
| 165 | case <-p.quit: | |
| 166 | default: | |
| 167 | close(p.quit) | |
| 168 | } | |
| 169 | } | |
| 170 | ||
| 164 | 171 | // server monitors underlying channels and renders any progress bars |
| 165 | 172 | func (p *Progress) server(conf pConf) { |
| 166 | 173 | |
| 167 | 174 | defer func() { |
| 168 | p.conf = conf | |
| 175 | // p.conf = conf | |
| 169 | 176 | if conf.shutdownNotifier != nil { |
| 170 | 177 | close(conf.shutdownNotifier) |
| 171 | 178 | } |
| 172 | 179 | close(p.done) |
| 173 | 180 | }() |
| 174 | ||
| 175 | // recoverFn := func(ch chan []byte) { | |
| 176 | // if p := recover(); p != nil { | |
| 177 | // ch <- []byte(fmt.Sprintln(p)) | |
| 178 | // } | |
| 179 | // close(ch) | |
| 180 | // } | |
| 181 | 181 | |
| 182 | 182 | for { |
| 183 | 183 | select { |
| 193 | 193 | conf.beforeRender(conf.bars) |
| 194 | 194 | } |
| 195 | 195 | |
| 196 | quitWidthSyncCh := make(chan struct{}) | |
| 196 | wSyncTimeout := make(chan struct{}) | |
| 197 | 197 | time.AfterFunc(conf.rr, func() { |
| 198 | close(quitWidthSyncCh) | |
| 198 | close(wSyncTimeout) | |
| 199 | 199 | }) |
| 200 | 200 | |
| 201 | 201 | b0 := conf.bars[0] |
| 202 | prependWs := newWidthSync(quitWidthSyncCh, numBars, b0.NumOfPrependers()) | |
| 203 | appendWs := newWidthSync(quitWidthSyncCh, numBars, b0.NumOfAppenders()) | |
| 202 | prependWs := newWidthSync(wSyncTimeout, numBars, b0.NumOfPrependers()) | |
| 203 | appendWs := newWidthSync(wSyncTimeout, numBars, b0.NumOfAppenders()) | |
| 204 | 204 | |
| 205 | 205 | tw, _, _ := cwriter.GetTermSize() |
| 206 | 206 | |
| 221 | 221 | case <-conf.cancel: |
| 222 | 222 | conf.ticker.Stop() |
| 223 | 223 | conf.cancel = nil |
| 224 | case <-p.stopReqCh: | |
| 225 | conf.ticker.Stop() | |
| 224 | case <-p.quit: | |
| 225 | if conf.cancel != nil { | |
| 226 | conf.ticker.Stop() | |
| 227 | } | |
| 226 | 228 | return |
| 227 | 229 | } |
| 228 | 230 | } |
| 229 | 231 | } |
| 230 | 232 | |
| 231 | func newWidthSync(quit <-chan struct{}, numBars, numColumn int) *widthSync { | |
| 233 | func newWidthSync(timeout <-chan struct{}, numBars, numColumn int) *widthSync { | |
| 232 | 234 | ws := &widthSync{ |
| 233 | 235 | listen: make([]chan int, numColumn), |
| 234 | 236 | result: make([]chan int, numColumn), |
| 249 | 251 | if len(widths) == numBars { |
| 250 | 252 | break loop |
| 251 | 253 | } |
| 252 | case <-quit: | |
| 254 | case <-timeout: | |
| 253 | 255 | if len(widths) == 0 { |
| 254 | 256 | return |
| 255 | 257 | } |
| 278 | 280 | return ch |
| 279 | 281 | } |
| 280 | 282 | |
| 281 | func updateConf(p *Progress, op func(*pConf)) *Progress { | |
| 282 | select { | |
| 283 | case p.ops <- op: | |
| 284 | return p | |
| 285 | case <-p.done: | |
| 286 | return nil | |
| 287 | } | |
| 288 | } | |
| 289 | ||
| 290 | 283 | func max(slice []int) int { |
| 291 | 284 | max := slice[0] |
| 292 | 285 | |