wait wor syncWidth before next render
Vladimir Bauer
3 years ago
| 182 | 182 | } |
| 183 | 183 | |
| 184 | 184 | for _, columnCase := range testCases { |
| 185 | mpb.SyncWidth(toSyncMatrix(columnCase)) | |
| 186 | numBars := len(columnCase) | |
| 187 | gott := make([]chan string, numBars) | |
| 188 | wg := new(sync.WaitGroup) | |
| 189 | wg.Add(numBars) | |
| 190 | for i, step := range columnCase { | |
| 185 | var wg sync.WaitGroup | |
| 186 | mpb.SyncWidth(&wg, toSyncMatrix(columnCase)) | |
| 187 | var results []chan string | |
| 188 | for _, step := range columnCase { | |
| 191 | 189 | step := step |
| 192 | ch := make(chan string, 1) | |
| 190 | ch := make(chan string) | |
| 193 | 191 | go func() { |
| 194 | defer wg.Done() | |
| 195 | 192 | ch <- step.decorator.Decor(step.stat) |
| 196 | 193 | }() |
| 197 | gott[i] = ch | |
| 194 | results = append(results, ch) | |
| 195 | } | |
| 196 | ||
| 197 | for i, ch := range results { | |
| 198 | res := <-ch | |
| 199 | want := columnCase[i].want | |
| 200 | if res != want { | |
| 201 | t.Errorf("Want: %q, Got: %q\n", want, res) | |
| 202 | } | |
| 198 | 203 | } |
| 199 | 204 | wg.Wait() |
| 200 | ||
| 201 | for i, ch := range gott { | |
| 202 | got := <-ch | |
| 203 | want := columnCase[i].want | |
| 204 | if got != want { | |
| 205 | t.Errorf("Want: %q, Got: %q\n", want, got) | |
| 206 | } | |
| 207 | } | |
| 208 | ||
| 209 | 205 | } |
| 210 | 206 | } |
| 211 | 207 |
| 1 | 1 | |
| 2 | 2 | // make syncWidth func public in test |
| 3 | 3 | var SyncWidth = syncWidth |
| 4 | var MaxWidthDistributor = maxWidthDistributor |
| 296 | 296 | } |
| 297 | 297 | |
| 298 | 298 | func (s *pState) render(cw *cwriter.Writer) error { |
| 299 | var wg sync.WaitGroup | |
| 299 | 300 | if s.heapUpdated { |
| 300 | 301 | s.updateSyncMatrix() |
| 301 | 302 | s.heapUpdated = false |
| 302 | 303 | } |
| 303 | syncWidth(s.pMatrix) | |
| 304 | syncWidth(s.aMatrix) | |
| 304 | syncWidth(&wg, s.pMatrix) | |
| 305 | syncWidth(&wg, s.aMatrix) | |
| 305 | 306 | |
| 306 | 307 | width, height, err := cw.GetTermSize() |
| 307 | 308 | if err != nil { |
| 313 | 314 | go bar.render(width) |
| 314 | 315 | } |
| 315 | 316 | |
| 316 | return s.flush(cw, height) | |
| 317 | } | |
| 318 | ||
| 319 | func (s *pState) flush(cw *cwriter.Writer, height int) error { | |
| 320 | var wg sync.WaitGroup | |
| 317 | err = s.flush(&wg, cw, height) | |
| 318 | wg.Wait() | |
| 319 | return err | |
| 320 | } | |
| 321 | ||
| 322 | func (s *pState) flush(wg *sync.WaitGroup, cw *cwriter.Writer, height int) error { | |
| 321 | 323 | var popCount int |
| 322 | 324 | |
| 323 | 325 | for s.bHeap.Len() > 0 { |
| 372 | 374 | } |
| 373 | 375 | case 1: |
| 374 | 376 | heap.Push(&s.bHeap, s.pool[0]) |
| 377 | s.pool = s.pool[:0] | |
| 375 | 378 | default: |
| 376 | 379 | wg.Add(1) |
| 377 | 380 | go func() { |
| 378 | 381 | for _, b := range s.pool { |
| 379 | 382 | heap.Push(&s.bHeap, b) |
| 380 | 383 | } |
| 384 | s.pool = s.pool[:0] | |
| 381 | 385 | wg.Done() |
| 382 | 386 | }() |
| 383 | 387 | } |
| 385 | 389 | for i := len(s.rows) - 1; i >= 0; i-- { |
| 386 | 390 | _, err := cw.ReadFrom(s.rows[i]) |
| 387 | 391 | if err != nil { |
| 388 | wg.Wait() | |
| 392 | s.rows = s.rows[:0] | |
| 389 | 393 | return err |
| 390 | 394 | } |
| 391 | 395 | } |
| 392 | 396 | |
| 393 | 397 | err := cw.Flush(len(s.rows) - popCount) |
| 394 | wg.Wait() | |
| 395 | 398 | s.rows = s.rows[:0] |
| 396 | s.pool = s.pool[:0] | |
| 397 | 399 | return err |
| 398 | 400 | } |
| 399 | 401 | |
| 482 | 484 | return bs |
| 483 | 485 | } |
| 484 | 486 | |
| 485 | func syncWidth(matrix map[int][]chan int) { | |
| 487 | func syncWidth(wg *sync.WaitGroup, matrix map[int][]chan int) { | |
| 486 | 488 | for _, column := range matrix { |
| 487 | go maxWidthDistributor(column) | |
| 488 | } | |
| 489 | } | |
| 490 | ||
| 491 | func maxWidthDistributor(column []chan int) { | |
| 489 | wg.Add(1) | |
| 490 | go maxWidthDistributor(wg, column) | |
| 491 | } | |
| 492 | } | |
| 493 | ||
| 494 | func maxWidthDistributor(wg *sync.WaitGroup, column []chan int) { | |
| 492 | 495 | var maxWidth int |
| 493 | 496 | for _, ch := range column { |
| 494 | 497 | if w := <-ch; w > maxWidth { |
| 498 | 501 | for _, ch := range column { |
| 499 | 502 | ch <- maxWidth |
| 500 | 503 | } |
| 501 | } | |
| 504 | wg.Done() | |
| 505 | } | |
| 111 | 111 | } |
| 112 | 112 | } |
| 113 | 113 | |
| 114 | func TestMaxWidthDistributor(t *testing.T) { | |
| 115 | makeWrapper := func(f func([]chan int), start, end chan struct{}) func([]chan int) { | |
| 116 | return func(column []chan int) { | |
| 117 | start <- struct{}{} | |
| 118 | f(column) | |
| 119 | <-end | |
| 120 | } | |
| 121 | } | |
| 122 | ||
| 123 | ready := make(chan struct{}) | |
| 124 | start := make(chan struct{}) | |
| 125 | end := make(chan struct{}) | |
| 126 | // mpb.MaxWidthDistributor shouldn't stuck in the middle while removing or aborting a bar | |
| 127 | mpb.MaxWidthDistributor = makeWrapper(mpb.MaxWidthDistributor, start, end) | |
| 128 | ||
| 129 | total := 100 | |
| 130 | numBars := 6 | |
| 131 | p := mpb.New(mpb.WithOutput(io.Discard)) | |
| 132 | for i := 0; i < numBars; i++ { | |
| 133 | bar := p.AddBar(int64(total), | |
| 134 | mpb.BarOptional(mpb.BarRemoveOnComplete(), i == 0), | |
| 135 | mpb.PrependDecorators(decor.EwmaETA(decor.ET_STYLE_GO, 60, decor.WCSyncSpace)), | |
| 136 | ) | |
| 137 | go func() { | |
| 138 | <-ready | |
| 139 | for i := 0; i < total; i++ { | |
| 140 | start := time.Now() | |
| 141 | if id := bar.ID(); id > 1 && i >= 32 { | |
| 142 | if id&1 == 1 { | |
| 143 | bar.Abort(true) | |
| 144 | } else { | |
| 145 | bar.Abort(false) | |
| 146 | } | |
| 147 | } | |
| 148 | time.Sleep(randomDuration(100 * time.Millisecond)) | |
| 149 | bar.EwmaIncrInt64(rand.Int63n(5)+1, time.Since(start)) | |
| 150 | } | |
| 151 | }() | |
| 152 | } | |
| 153 | ||
| 154 | go func() { | |
| 155 | <-ready | |
| 156 | p.Wait() | |
| 157 | close(start) | |
| 158 | }() | |
| 159 | ||
| 160 | res := t.Run("maxWidthDistributor", func(t *testing.T) { | |
| 161 | close(ready) | |
| 162 | for v := range start { | |
| 163 | timer := time.NewTimer(100 * time.Millisecond) | |
| 164 | select { | |
| 165 | case end <- v: | |
| 166 | timer.Stop() | |
| 167 | case <-timer.C: | |
| 168 | t.FailNow() | |
| 169 | } | |
| 170 | } | |
| 171 | }) | |
| 172 | ||
| 173 | if !res { | |
| 174 | t.Error("maxWidthDistributor stuck in the middle") | |
| 175 | } | |
| 176 | } | |
| 177 | ||
| 178 | 114 | func TestProgressShutdownsWithErrFiller(t *testing.T) { |
| 179 | 115 | var debug bytes.Buffer |
| 180 | 116 | shutdown := make(chan struct{}) |