pass state around
Vladimir Bauer
9 years ago
| 0 | 0 | package mpb |
| 1 | 1 | |
| 2 | 2 | import ( |
| 3 | "fmt" | |
| 3 | 4 | "io" |
| 4 | 5 | "math" |
| 5 | 6 | "sync" |
| 20 | 21 | |
| 21 | 22 | // Bar represents a progress Bar |
| 22 | 23 | type Bar struct { |
| 23 | stateReqCh chan chan state | |
| 24 | widthCh chan int | |
| 25 | formatCh chan string | |
| 26 | etaAlphaCh chan float64 | |
| 24 | stateReqCh chan state | |
| 27 | 25 | incrCh chan int64 |
| 28 | trimLeftCh chan bool | |
| 29 | trimRightCh chan bool | |
| 30 | refillCh chan *refill | |
| 31 | 26 | dCommandCh chan *dCommandData |
| 32 | 27 | flushedCh chan struct{} |
| 33 | 28 | removeReqCh chan struct{} |
| 34 | 29 | completeReqCh chan struct{} |
| 35 | 30 | done chan struct{} |
| 31 | cancel <-chan struct{} | |
| 36 | 32 | |
| 37 | 33 | // follawing are used after (*Bar.done) is closed |
| 38 | 34 | width int |
| 45 | 41 | Completed bool |
| 46 | 42 | Total int64 |
| 47 | 43 | Current int64 |
| 48 | IncrAmount int64 | |
| 49 | 44 | StartTime time.Time |
| 50 | 45 | TimeElapsed time.Duration |
| 51 | 46 | TimePerItemEstimate time.Duration |
| 68 | 63 | etaAlpha float64 |
| 69 | 64 | total int64 |
| 70 | 65 | current int64 |
| 71 | incrAmount int64 | |
| 72 | 66 | trimLeftSpace bool |
| 73 | 67 | trimRightSpace bool |
| 74 | 68 | completed bool |
| 84 | 78 | |
| 85 | 79 | func newBar(id int, total int64, wg *sync.WaitGroup, conf *userConf) *Bar { |
| 86 | 80 | b := &Bar{ |
| 87 | stateReqCh: make(chan chan state), | |
| 88 | widthCh: make(chan int), | |
| 89 | formatCh: make(chan string), | |
| 90 | etaAlphaCh: make(chan float64), | |
| 81 | width: conf.width, | |
| 82 | stateReqCh: make(chan state), | |
| 91 | 83 | incrCh: make(chan int64, 1), |
| 92 | trimLeftCh: make(chan bool), | |
| 93 | trimRightCh: make(chan bool), | |
| 94 | refillCh: make(chan *refill), | |
| 95 | 84 | dCommandCh: make(chan *dCommandData), |
| 96 | 85 | flushedCh: make(chan struct{}, 1), |
| 97 | 86 | removeReqCh: make(chan struct{}), |
| 98 | 87 | completeReqCh: make(chan struct{}), |
| 99 | 88 | done: make(chan struct{}), |
| 100 | } | |
| 101 | go b.server(id, total, wg, conf) | |
| 89 | cancel: conf.cancel, | |
| 90 | } | |
| 91 | ||
| 92 | s := state{ | |
| 93 | id: id, | |
| 94 | total: total, | |
| 95 | width: conf.width, | |
| 96 | etaAlpha: 0.25, | |
| 97 | } | |
| 98 | ||
| 99 | if total <= 0 { | |
| 100 | s.simpleSpinner = getSpinner() | |
| 101 | } else { | |
| 102 | s.updateFormat(conf.format) | |
| 103 | } | |
| 104 | ||
| 105 | go b.server(wg, s) | |
| 102 | 106 | return b |
| 103 | 107 | } |
| 104 | 108 | |
| 107 | 111 | if n < 2 || isClosed(b.done) { |
| 108 | 112 | return b |
| 109 | 113 | } |
| 110 | b.widthCh <- n | |
| 114 | s := <-b.stateReqCh | |
| 115 | s.width = n | |
| 116 | b.stateReqCh <- s | |
| 111 | 117 | return b |
| 112 | 118 | } |
| 113 | 119 | |
| 116 | 122 | if isClosed(b.done) { |
| 117 | 123 | return b |
| 118 | 124 | } |
| 119 | b.trimLeftCh <- true | |
| 125 | // b.trimLeftCh <- true | |
| 126 | s := <-b.stateReqCh | |
| 127 | s.trimLeftSpace = true | |
| 128 | b.stateReqCh <- s | |
| 120 | 129 | return b |
| 121 | 130 | } |
| 122 | 131 | |
| 125 | 134 | if isClosed(b.done) { |
| 126 | 135 | return b |
| 127 | 136 | } |
| 128 | b.trimRightCh <- true | |
| 137 | // b.trimRightCh <- true | |
| 138 | s := <-b.stateReqCh | |
| 139 | s.trimRightSpace = true | |
| 140 | b.stateReqCh <- s | |
| 129 | 141 | return b |
| 130 | 142 | } |
| 131 | 143 | |
| 134 | 146 | if utf8.RuneCountInString(format) != numFmtRunes || isClosed(b.done) { |
| 135 | 147 | return b |
| 136 | 148 | } |
| 137 | b.formatCh <- format | |
| 149 | s := <-b.stateReqCh | |
| 150 | s.updateFormat(format) | |
| 151 | b.stateReqCh <- s | |
| 138 | 152 | return b |
| 139 | 153 | } |
| 140 | 154 | |
| 145 | 159 | if isClosed(b.done) { |
| 146 | 160 | return b |
| 147 | 161 | } |
| 148 | b.etaAlphaCh <- a | |
| 162 | s := <-b.stateReqCh | |
| 163 | s.etaAlpha = a | |
| 164 | b.stateReqCh <- s | |
| 149 | 165 | return b |
| 150 | 166 | } |
| 151 | 167 | |
| 168 | 184 | return |
| 169 | 185 | } |
| 170 | 186 | b.Incr(n) |
| 171 | b.refillCh <- &refill{r, int64(n)} | |
| 187 | // b.refillCh <- &refill{r, int64(n)} | |
| 188 | s := <-b.stateReqCh | |
| 189 | s.refill = &refill{r, int64(n)} | |
| 190 | b.stateReqCh <- s | |
| 172 | 191 | } |
| 173 | 192 | |
| 174 | 193 | // GetAppenders returns slice of appender DecoratorFunc |
| 257 | 276 | if isClosed(b.done) { |
| 258 | 277 | return b.state |
| 259 | 278 | } |
| 260 | ch := make(chan state) | |
| 261 | b.stateReqCh <- ch | |
| 262 | return <-ch | |
| 263 | } | |
| 264 | ||
| 265 | func (b *Bar) server(id int, total int64, wg *sync.WaitGroup, conf *userConf) { | |
| 279 | return <-b.stateReqCh | |
| 280 | } | |
| 281 | ||
| 282 | func (b *Bar) server(wg *sync.WaitGroup, s state) { | |
| 266 | 283 | var incrStartTime time.Time |
| 267 | barState := state{ | |
| 268 | id: id, | |
| 269 | total: total, | |
| 270 | width: conf.width, | |
| 271 | etaAlpha: 0.25, | |
| 272 | } | |
| 273 | if total <= 0 { | |
| 274 | barState.simpleSpinner = getSpinner() | |
| 275 | } else { | |
| 276 | barState.updateFormat(conf.format) | |
| 277 | } | |
| 284 | ||
| 278 | 285 | defer func() { |
| 279 | b.stop(&barState, conf.width) | |
| 286 | // b.stop(&barState, conf.width) | |
| 287 | b.stop(&s) | |
| 280 | 288 | wg.Done() |
| 289 | fmt.Printf("Exited bar %d\n", s.id) | |
| 281 | 290 | }() |
| 291 | ||
| 282 | 292 | for { |
| 283 | 293 | select { |
| 284 | case barState.incrAmount = <-b.incrCh: | |
| 285 | if barState.current == 0 { | |
| 294 | case b.stateReqCh <- s: | |
| 295 | case s = <-b.stateReqCh: | |
| 296 | case amount := <-b.incrCh: | |
| 297 | if s.current == 0 { | |
| 286 | 298 | incrStartTime = time.Now() |
| 287 | barState.startTime = incrStartTime | |
| 299 | s.startTime = incrStartTime | |
| 288 | 300 | } |
| 289 | n := barState.current + barState.incrAmount | |
| 290 | if total > 0 && n > total { | |
| 291 | barState.current = total | |
| 292 | barState.completed = true | |
| 301 | n := s.current + amount | |
| 302 | if s.total > 0 && n > s.total { | |
| 303 | s.current = s.total | |
| 304 | s.completed = true | |
| 293 | 305 | break // break out of select |
| 294 | 306 | } |
| 295 | barState.timeElapsed = time.Since(barState.startTime) | |
| 296 | barState.updateTimePerItemEstimate(incrStartTime) | |
| 297 | if n == total { | |
| 298 | barState.completed = true | |
| 307 | s.timeElapsed = time.Since(s.startTime) | |
| 308 | s.updateTimePerItemEstimate(incrStartTime, amount) | |
| 309 | if n == s.total { | |
| 310 | s.completed = true | |
| 299 | 311 | } |
| 300 | barState.current = n | |
| 312 | s.current = n | |
| 301 | 313 | incrStartTime = time.Now() |
| 302 | 314 | case data := <-b.dCommandCh: |
| 303 | 315 | switch data.action { |
| 304 | 316 | case dAppend: |
| 305 | barState.appendFuncs = append(barState.appendFuncs, data.f) | |
| 317 | s.appendFuncs = append(s.appendFuncs, data.f) | |
| 306 | 318 | case dAppendZero: |
| 307 | barState.appendFuncs = nil | |
| 319 | s.appendFuncs = nil | |
| 308 | 320 | case dPrepend: |
| 309 | barState.prependFuncs = append(barState.prependFuncs, data.f) | |
| 321 | s.prependFuncs = append(s.prependFuncs, data.f) | |
| 310 | 322 | case dPrependZero: |
| 311 | barState.prependFuncs = nil | |
| 323 | s.prependFuncs = nil | |
| 312 | 324 | } |
| 313 | case ch := <-b.stateReqCh: | |
| 314 | ch <- barState | |
| 315 | case format := <-b.formatCh: | |
| 316 | barState.updateFormat(format) | |
| 317 | case barState.width = <-b.widthCh: | |
| 318 | case barState.refill = <-b.refillCh: | |
| 319 | case barState.trimLeftSpace = <-b.trimLeftCh: | |
| 320 | case barState.trimRightSpace = <-b.trimRightCh: | |
| 321 | 325 | case <-b.flushedCh: |
| 322 | if barState.completed { | |
| 326 | if s.completed { | |
| 323 | 327 | return |
| 324 | 328 | } |
| 325 | 329 | case <-b.completeReqCh: |
| 326 | 330 | return |
| 327 | 331 | case <-b.removeReqCh: |
| 328 | 332 | return |
| 329 | case <-conf.cancel: | |
| 333 | case <-b.cancel: | |
| 330 | 334 | return |
| 331 | 335 | } |
| 332 | 336 | } |
| 333 | 337 | } |
| 334 | 338 | |
| 335 | func (b *Bar) stop(s *state, width int) { | |
| 339 | func (b *Bar) stop(s *state) { | |
| 336 | 340 | b.state = *s |
| 337 | b.width = width | |
| 338 | 341 | close(b.done) |
| 339 | 342 | } |
| 340 | 343 | |
| 373 | 376 | } |
| 374 | 377 | } |
| 375 | 378 | |
| 376 | func (s *state) updateTimePerItemEstimate(incrStartTime time.Time) { | |
| 379 | func (s *state) updateTimePerItemEstimate(incrStartTime time.Time, amount int64) { | |
| 377 | 380 | lastBlockTime := time.Since(incrStartTime) // shorthand for time.Now().Sub(t) |
| 378 | lastItemEstimate := float64(lastBlockTime) / float64(s.incrAmount) | |
| 381 | lastItemEstimate := float64(lastBlockTime) / float64(amount) | |
| 379 | 382 | s.timePerItem = time.Duration((s.etaAlpha * lastItemEstimate) + (1-s.etaAlpha)*float64(s.timePerItem)) |
| 380 | 383 | } |
| 381 | 384 | |
| 497 | 500 | Completed: s.completed, |
| 498 | 501 | Total: s.total, |
| 499 | 502 | Current: s.current, |
| 500 | IncrAmount: s.incrAmount, | |
| 501 | 503 | StartTime: s.startTime, |
| 502 | 504 | TimeElapsed: s.timeElapsed, |
| 503 | 505 | TimePerItemEstimate: s.timePerItem, |