diff --git a/export_test.go b/export_test.go index 7f5cb84..fba0eaf 100644 --- a/export_test.go +++ b/export_test.go @@ -2,3 +2,4 @@ // make syncWidth func public in test var SyncWidth = syncWidth +var MaxWidthDistributor = &maxWidthDistributor diff --git a/progress.go b/progress.go index 983ca20..2a61480 100644 --- a/progress.go +++ b/progress.go @@ -372,17 +372,18 @@ func syncWidth(matrix map[int][]chan int) { for _, column := range matrix { - column := column - go func() { - var maxWidth int - for _, ch := range column { - if w := <-ch; w > maxWidth { - maxWidth = w - } - } - for _, ch := range column { - ch <- maxWidth - } - }() - } -} + go maxWidthDistributor(column) + } +} + +var maxWidthDistributor = func(column []chan int) { + var maxWidth int + for _, ch := range column { + if w := <-ch; w > maxWidth { + maxWidth = w + } + } + for _, ch := range column { + ch <- maxWidth + } +} diff --git a/progress_test.go b/progress_test.go index 3390d2c..336612d 100644 --- a/progress_test.go +++ b/progress_test.go @@ -10,6 +10,7 @@ "time" "github.com/vbauerster/mpb/v5" + "github.com/vbauerster/mpb/v5/decor" ) func init() { @@ -23,12 +24,13 @@ wg.Add(1) b := p.AddBar(100) go func() { + rng := rand.New(rand.NewSource(time.Now().UnixNano())) for i := 0; i < 100; i++ { if i == 33 { wg.Done() } b.Increment() - time.Sleep(randomDuration(100 * time.Millisecond)) + time.Sleep((time.Duration(rng.Intn(10)+1) * (10 * time.Millisecond)) / 2) } }() @@ -50,7 +52,7 @@ bars := make([]*mpb.Bar, 3) for i := 0; i < 3; i++ { b := p.AddBar(100) - bars[i] = b + rng := rand.New(rand.NewSource(time.Now().UnixNano())) go func(n int) { for i := 0; !b.Completed(); i++ { if n == 0 && i >= 33 { @@ -58,9 +60,10 @@ wg.Done() } b.Increment() - time.Sleep(randomDuration(100 * time.Millisecond)) + time.Sleep((time.Duration(rng.Intn(10)+1) * (10 * time.Millisecond)) / 2) } }(i) + bars[i] = b } wg.Wait() @@ -107,6 +110,71 @@ } } +// MaxWidthDistributor shouldn't stuck in the middle while removing or aborting a bar +func TestMaxWidthDistributor(t *testing.T) { + + makeWrapper := func(f func([]chan int), start, end chan struct{}) func([]chan int) { + return func(column []chan int) { + start <- struct{}{} + f(column) + <-end + } + } + + ready := make(chan struct{}) + start := make(chan struct{}) + end := make(chan struct{}) + *mpb.MaxWidthDistributor = makeWrapper(*mpb.MaxWidthDistributor, start, end) + + total := 80 + numBars := 3 + p := mpb.New(mpb.WithOutput(ioutil.Discard)) + for i := 0; i < numBars; i++ { + bar := p.AddBar(int64(total), + mpb.BarOptOn(mpb.BarRemoveOnComplete(), func() bool { return i == 0 }), + mpb.PrependDecorators( + decor.EwmaETA(decor.ET_STYLE_GO, 60, decor.WCSyncSpace), + ), + ) + go func() { + <-ready + rng := rand.New(rand.NewSource(time.Now().UnixNano())) + for i := 0; i < total; i++ { + start := time.Now() + if bar.ID() >= numBars-1 && i >= 42 { + bar.Abort(true) + } + time.Sleep((time.Duration(rng.Intn(10)+1) * (10 * time.Millisecond)) / 2) + bar.Increment() + bar.DecoratorEwmaUpdate(time.Since(start)) + } + }() + } + + go func() { + <-ready + p.Wait() + close(start) + }() + + res := t.Run("maxWidthDistributor", func(t *testing.T) { + close(ready) + for v := range start { + timer := time.NewTimer(100 * time.Millisecond) + select { + case end <- v: + timer.Stop() + case <-timer.C: + t.FailNow() + } + } + }) + + if !res { + t.Error("maxWidthDistributor stuck in the middle") + } +} + func getLastLine(bb []byte) []byte { split := bytes.Split(bb, []byte("\n")) return split[len(split)-2]