Refactor: widthSyncer
Vladimir Bauer
8 years ago
| 61 | 61 | timeElapsed time.Duration |
| 62 | 62 | blockStartTime time.Time |
| 63 | 63 | timePerItem time.Duration |
| 64 | appendFuncs []decor.DecoratorFunc | |
| 65 | prependFuncs []decor.DecoratorFunc | |
| 64 | aDecorators []decor.DecoratorFunc | |
| 65 | pDecorators []decor.DecoratorFunc | |
| 66 | 66 | refill *refill |
| 67 | 67 | bufP, bufB, bufA *bytes.Buffer |
| 68 | 68 | panicMsg string |
| 112 | 112 | func (b *Bar) RemoveAllPrependers() { |
| 113 | 113 | select { |
| 114 | 114 | case b.operateState <- func(s *bState) { |
| 115 | s.prependFuncs = nil | |
| 115 | s.pDecorators = nil | |
| 116 | 116 | }: |
| 117 | 117 | case <-b.done: |
| 118 | 118 | } |
| 122 | 122 | func (b *Bar) RemoveAllAppenders() { |
| 123 | 123 | select { |
| 124 | 124 | case b.operateState <- func(s *bState) { |
| 125 | s.appendFuncs = nil | |
| 125 | s.aDecorators = nil | |
| 126 | 126 | }: |
| 127 | 127 | case <-b.done: |
| 128 | 128 | } |
| 195 | 195 | func (b *Bar) NumOfAppenders() int { |
| 196 | 196 | result := make(chan int, 1) |
| 197 | 197 | select { |
| 198 | case b.operateState <- func(s *bState) { result <- len(s.appendFuncs) }: | |
| 199 | return <-result | |
| 200 | case <-b.done: | |
| 201 | return len(b.cacheState.appendFuncs) | |
| 198 | case b.operateState <- func(s *bState) { result <- len(s.aDecorators) }: | |
| 199 | return <-result | |
| 200 | case <-b.done: | |
| 201 | return len(b.cacheState.aDecorators) | |
| 202 | 202 | } |
| 203 | 203 | } |
| 204 | 204 | |
| 206 | 206 | func (b *Bar) NumOfPrependers() int { |
| 207 | 207 | result := make(chan int, 1) |
| 208 | 208 | select { |
| 209 | case b.operateState <- func(s *bState) { result <- len(s.prependFuncs) }: | |
| 210 | return <-result | |
| 211 | case <-b.done: | |
| 212 | return len(b.cacheState.prependFuncs) | |
| 209 | case b.operateState <- func(s *bState) { result <- len(s.pDecorators) }: | |
| 210 | return <-result | |
| 211 | case <-b.done: | |
| 212 | return len(b.cacheState.pDecorators) | |
| 213 | 213 | } |
| 214 | 214 | } |
| 215 | 215 | |
| 306 | 306 | } |
| 307 | 307 | } |
| 308 | 308 | |
| 309 | func (b *Bar) render(tw int, prependWs, appendWs *widthSync) <-chan *toRenderReader { | |
| 309 | func (b *Bar) render(tw int, pSyncer, aSyncer *widthSyncer) <-chan *toRenderReader { | |
| 310 | 310 | ch := make(chan *toRenderReader, 1) |
| 311 | 311 | |
| 312 | 312 | go func() { |
| 317 | 317 | // recovering if external decorators panic |
| 318 | 318 | if p := recover(); p != nil { |
| 319 | 319 | s.panicMsg = fmt.Sprintf("b#%02d panic: %v\n", s.id, p) |
| 320 | s.prependFuncs = nil | |
| 321 | s.appendFuncs = nil | |
| 320 | s.pDecorators = nil | |
| 321 | s.aDecorators = nil | |
| 322 | 322 | s.completed = true |
| 323 | 323 | r = strings.NewReader(s.panicMsg) |
| 324 | 324 | } |
| 325 | 325 | ch <- &toRenderReader{r, s.completed, s.removed} |
| 326 | 326 | }() |
| 327 | s.draw(tw, prependWs, appendWs) | |
| 327 | s.draw(tw, pSyncer, aSyncer) | |
| 328 | 328 | r = io.MultiReader(s.bufP, s.bufB, s.bufA) |
| 329 | 329 | }: |
| 330 | 330 | case <-b.done: |
| 333 | 333 | if s.panicMsg != "" { |
| 334 | 334 | r = strings.NewReader(s.panicMsg) |
| 335 | 335 | } else { |
| 336 | s.draw(tw, prependWs, appendWs) | |
| 336 | s.draw(tw, pSyncer, aSyncer) | |
| 337 | 337 | r = io.MultiReader(s.bufP, s.bufB, s.bufA) |
| 338 | 338 | } |
| 339 | 339 | ch <- &toRenderReader{r, s.completed, s.removed} |
| 350 | 350 | s.blockStartTime = next |
| 351 | 351 | } |
| 352 | 352 | |
| 353 | func (s *bState) draw(termWidth int, prependWs, appendWs *widthSync) { | |
| 353 | func (s *bState) draw(termWidth int, pSyncer, aSyncer *widthSyncer) { | |
| 354 | 354 | if termWidth <= 0 { |
| 355 | 355 | termWidth = s.width |
| 356 | 356 | } |
| 359 | 359 | |
| 360 | 360 | // render prepend functions to the left of the bar |
| 361 | 361 | s.bufP.Reset() |
| 362 | for i, f := range s.prependFuncs { | |
| 363 | s.bufP.WriteString(f(stat, prependWs.Listen[i], prependWs.Result[i])) | |
| 362 | for i, f := range s.pDecorators { | |
| 363 | s.bufP.WriteString(f(stat, pSyncer.Accumulator[i], pSyncer.Distributor[i])) | |
| 364 | 364 | } |
| 365 | 365 | |
| 366 | 366 | if !s.trimLeftSpace { |
| 373 | 373 | s.bufA.WriteByte(' ') |
| 374 | 374 | } |
| 375 | 375 | |
| 376 | for i, f := range s.appendFuncs { | |
| 377 | s.bufA.WriteString(f(stat, appendWs.Listen[i], appendWs.Result[i])) | |
| 376 | for i, f := range s.aDecorators { | |
| 377 | s.bufA.WriteString(f(stat, aSyncer.Accumulator[i], aSyncer.Distributor[i])) | |
| 378 | 378 | } |
| 379 | 379 | |
| 380 | 380 | prependCount := utf8.RuneCount(s.bufP.Bytes()) |
| 8 | 8 | // AppendDecorators let you inject decorators to the bar's right side |
| 9 | 9 | func AppendDecorators(appenders ...decor.DecoratorFunc) BarOption { |
| 10 | 10 | return func(s *bState) { |
| 11 | s.appendFuncs = append(s.appendFuncs, appenders...) | |
| 11 | s.aDecorators = append(s.aDecorators, appenders...) | |
| 12 | 12 | } |
| 13 | 13 | } |
| 14 | 14 | |
| 15 | 15 | // PrependDecorators let you inject decorators to the bar's left side |
| 16 | 16 | func PrependDecorators(prependers ...decor.DecoratorFunc) BarOption { |
| 17 | 17 | return func(s *bState) { |
| 18 | s.prependFuncs = append(s.prependFuncs, prependers...) | |
| 18 | s.pDecorators = append(s.pDecorators, prependers...) | |
| 19 | 19 | } |
| 20 | 20 | } |
| 21 | 21 |
| 126 | 126 | res[i] = make(chan string, 1) |
| 127 | 127 | go func(s step, ch chan string) { |
| 128 | 128 | defer wg.Done() |
| 129 | ch <- dfn(s.stat, ws.Listen[0], ws.Result[0]) | |
| 129 | ch <- dfn(s.stat, ws.Accumulator[0], ws.Distributor[0]) | |
| 130 | 130 | }(columnCase[i], res[i]) |
| 131 | 131 | } |
| 132 | 132 | wg.Wait() |
| 177 | 177 | }, |
| 178 | 178 | } |
| 179 | 179 | |
| 180 | prependWs := newWidthSync(nil, 1, 0) | |
| 181 | appendWs := newWidthSync(nil, 1, 0) | |
| 180 | prependWs := newWidthSyncer(nil, 1, 0) | |
| 181 | appendWs := newWidthSyncer(nil, 1, 0) | |
| 182 | 182 | for termWidth, cases := range testSuite { |
| 183 | 183 | for name, tc := range cases { |
| 184 | 184 | s := newTestState() |
| 50 | 50 | shutdownNotifier chan struct{} |
| 51 | 51 | cancel <-chan struct{} |
| 52 | 52 | } |
| 53 | widthSync struct { | |
| 54 | Listen []chan int | |
| 55 | Result []chan int | |
| 53 | widthSyncer struct { | |
| 54 | // Public for easy testing | |
| 55 | Accumulator []chan int | |
| 56 | Distributor []chan int | |
| 56 | 57 | } |
| 57 | 58 | toRenderSnapshot struct { |
| 58 | 59 | bar *Bar |
| 154 | 155 | <-p.done |
| 155 | 156 | } |
| 156 | 157 | |
| 157 | func newWidthSync(timeout <-chan struct{}, numBars, numColumn int) *widthSync { | |
| 158 | ws := &widthSync{ | |
| 159 | Listen: make([]chan int, numColumn), | |
| 160 | Result: make([]chan int, numColumn), | |
| 158 | func newWidthSyncer(timeout <-chan struct{}, numBars, numColumn int) *widthSyncer { | |
| 159 | ws := &widthSyncer{ | |
| 160 | Accumulator: make([]chan int, numColumn), | |
| 161 | Distributor: make([]chan int, numColumn), | |
| 161 | 162 | } |
| 162 | 163 | for i := 0; i < numColumn; i++ { |
| 163 | ws.Listen[i] = make(chan int, numBars) | |
| 164 | ws.Result[i] = make(chan int, numBars) | |
| 164 | ws.Accumulator[i] = make(chan int, numBars) | |
| 165 | ws.Distributor[i] = make(chan int, numBars) | |
| 165 | 166 | } |
| 166 | 167 | for i := 0; i < numColumn; i++ { |
| 167 | go func(listenCh <-chan int, resultCh chan<- int) { | |
| 168 | defer close(resultCh) | |
| 168 | go func(accumulator <-chan int, discharger chan<- int) { | |
| 169 | defer close(discharger) | |
| 169 | 170 | widths := make([]int, 0, numBars) |
| 170 | 171 | loop: |
| 171 | 172 | for { |
| 172 | 173 | select { |
| 173 | case w := <-listenCh: | |
| 174 | case w := <-accumulator: | |
| 174 | 175 | widths = append(widths, w) |
| 175 | 176 | if len(widths) == numBars { |
| 176 | 177 | break loop |
| 184 | 185 | } |
| 185 | 186 | result := max(widths) |
| 186 | 187 | for i := 0; i < len(widths); i++ { |
| 187 | resultCh <- result | |
| 188 | discharger <- result | |
| 188 | 189 | } |
| 189 | }(ws.Listen[i], ws.Result[i]) | |
| 190 | }(ws.Accumulator[i], ws.Distributor[i]) | |
| 190 | 191 | } |
| 191 | 192 | return ws |
| 192 | 193 | } |
| 193 | 194 | |
| 194 | 195 | func (s *pState) writeAndFlush(tw, numP, numA int) (err error) { |
| 195 | wSyncTimeout := make(chan struct{}) | |
| 196 | timeout := make(chan struct{}) | |
| 197 | pSyncer := newWidthSyncer(timeout, s.bHeap.Len(), numP) | |
| 198 | aSyncer := newWidthSyncer(timeout, s.bHeap.Len(), numA) | |
| 196 | 199 | time.AfterFunc(s.rr-s.rr/12, func() { |
| 197 | close(wSyncTimeout) | |
| 200 | close(timeout) | |
| 198 | 201 | }) |
| 199 | 202 | |
| 200 | prependWs := newWidthSync(wSyncTimeout, s.bHeap.Len(), numP) | |
| 201 | appendWs := newWidthSync(wSyncTimeout, s.bHeap.Len(), numA) | |
| 202 | ||
| 203 | for _, trs := range s.renderByPriority(tw, prependWs, appendWs) { | |
| 203 | for _, trs := range s.renderByPriority(tw, pSyncer, aSyncer) { | |
| 204 | 204 | r := <-trs.pipe |
| 205 | 205 | _, err = s.cw.ReadFrom(r) |
| 206 | 206 | if !trs.bar.completed && r.toComplete { |
| 222 | 222 | return |
| 223 | 223 | } |
| 224 | 224 | |
| 225 | func (s *pState) renderByPriority(tw int, prependWs, appendWs *widthSync) []*toRenderSnapshot { | |
| 225 | func (s *pState) renderByPriority(tw int, pSyncer, aSyncer *widthSyncer) []*toRenderSnapshot { | |
| 226 | 226 | slice := make([]*toRenderSnapshot, 0, s.bHeap.Len()) |
| 227 | 227 | for s.bHeap.Len() > 0 { |
| 228 | 228 | b := heap.Pop(s.bHeap).(*Bar) |
| 229 | 229 | defer heap.Push(s.bHeap, b) |
| 230 | 230 | slice = append(slice, &toRenderSnapshot{ |
| 231 | 231 | bar: b, |
| 232 | pipe: b.render(tw, prependWs, appendWs), | |
| 232 | pipe: b.render(tw, pSyncer, aSyncer), | |
| 233 | 233 | }) |
| 234 | 234 | } |
| 235 | 235 | return slice |