vbauerster / mpb

multi progress bar for Go cli applications
The Unlicense
2.29k stars 123 forks source link

what could possibly cause 2 bars to overlap? #91

Closed nikooo777 closed 3 years ago

nikooo777 commented 3 years ago

Hello, I have a quite complex software and I'm trying to display multiple progress bars.

This is the part of the code that is responsible for spawning the bars:

        ticker := time.NewTicker(400 * time.Millisecond)
        done := make(chan bool)
        go func() {
            ...
            v.progressBarWg.Add(1)
            bar := v.progressBars.AddBar(int64(totalSize),
                mpb.PrependDecorators(
                    decor.CountersKibiByte("% .2f / % .2f "),
                    // simple name decorator
                    decor.Name(fmt.Sprintf("id: %s src-ip: (%s)", v.id, sourceAddress)),
                    // decor.DSyncWidth bit enables column width synchronization
                    decor.Percentage(decor.WCSyncSpace),
                ),
                mpb.AppendDecorators(
                    decor.EwmaETA(decor.ET_STYLE_GO, 90),
                    decor.Name(" ] "),
                    decor.EwmaSpeed(decor.UnitKiB, "% .2f ", 60),
                ),
            )

            for {
                select {
                case <-done:
                    bar.Completed()
                    bar.Abort(true) //to avoid a deadlock if the size is wrong
                    v.progressBarWg.Done()
                    return
                case <-ticker.C:
                    size, err := logUtils.DirSize(v.videoDir())
                    if err != nil {
                        log.Errorf("error while getting size of download directory: %s", errors.FullTrace(err))
                        return
                    }
                    bar.SetCurrent(size)
                    bar.DecoratorEwmaUpdate(400 * time.Millisecond)
                }
            }
        }()

        ...
        //stop the progress bar
        ticker.Stop()
        done <- true

for some reasons the progress bars overlap and keep overwriting each other on the last line of the terminal. What am I doing wrong?

Thank you for your kind help.

Niko

nikooo777 commented 3 years ago

I managed to reproduce the issue in a snippet. Not really sure why but it seems like the ticker is throwing it off:

Edit: scratch that, this PoC is not right. I'm still looking into it

package main

import (
    "fmt"
    "sync"
    "time"

    "github.com/vbauerster/mpb/v7"
    "github.com/vbauerster/mpb/v7/decor"
)

func main() {
    var pbWg sync.WaitGroup
    // passed &wg will be accounted at p.Wait() call
    p := mpb.New(mpb.WithWaitGroup(&pbWg))

    for i := 0; i < 2; i++ {
        ticker := time.NewTicker(400 * time.Millisecond)
        done := make(chan bool)
        pbWg.Add(1)
        go func(id int) {
            bar := p.AddBar(int64(1024*1024*1024),
                mpb.PrependDecorators(
                    decor.CountersKibiByte("% .2f / % .2f "),
                    // simple name decorator
                    decor.Name(fmt.Sprintf("id: %d src-ip: (%s)", id, "test")),
                    // decor.DSyncWidth bit enables column width synchronization
                    decor.Percentage(decor.WCSyncSpace),
                ),
                mpb.AppendDecorators(
                    decor.EwmaETA(decor.ET_STYLE_GO, 90),
                    decor.Name(" ] "),
                    decor.EwmaSpeed(decor.UnitKiB, "% .2f ", 60),
                ),
            )
            val := int64(0)
            for {
                select {
                case <-done:
                    bar.Completed()
                    bar.Abort(true)
                    return
                case <-ticker.C:
                    val +=100
                    bar.SetCurrent(val)
                    bar.DecoratorEwmaUpdate(400 * time.Millisecond)
                }
            }
        }(i)
        time.Sleep(10*time.Second)
        //stop the progress bar
        ticker.Stop()
        done <- true
    }

    // Waiting for passed &wg and for all bars to complete and flush
    p.Wait()
}
nikooo777 commented 3 years ago

okay nevermind it, I found the reason. I was actually creating multiple instances and multiple waitgroups. Now everything is running smoothly! Sorry for the false alarm

vbauerster commented 3 years ago

Glad you figured that out. Though some notes: bar.Completed() is not meant to stop a bar its return value is bool. bar.Abort(..) is meant to interrupt a running bar. Not sure if that what you want. bar.SetCurrent(val) should be used only if total value is not know or if you know what you're doing. bar.DecoratorEwmaUpdate(400 * time.Millisecond) should be supplied with duration of a single iteration of work you measure. Supplying it with constant value on every iteration defeats its purpose.

Bar naturally stops itself when reaching its total value.