diff --git a/bar.go b/bar.go index 204276f..11ff51c 100644 --- a/bar.go +++ b/bar.go @@ -68,6 +68,7 @@ Alpha float64 incrRequestCh chan *incrRequest + incrCh chan int redrawRequestCh chan *redrawRequest @@ -79,8 +80,6 @@ stopCh chan struct{} done chan struct{} - - // wg *sync.WaitGroup } type Statistics struct { @@ -114,6 +113,7 @@ flushedCh: make(chan struct{}), stopCh: make(chan struct{}), done: make(chan struct{}), + incrCh: make(chan int), } go b.server(wg) return b @@ -155,27 +155,25 @@ // String returns the string representation of the bar func (b *Bar) String() string { - if b.IsStopped() { - return "bar stopped" - } + // if b.IsStopped() { + // return "bar stopped" + // } bufCh := make(chan []byte) b.redrawRequestCh <- &redrawRequest{bufCh} return string(<-bufCh) } func (b *Bar) flushed() { - if !b.IsStopped() { - b.flushedCh <- struct{}{} - } -} - -func (b *Bar) Incr(n int) bool { - if b.IsStopped() { - return false - } - result := make(chan bool) - b.incrRequestCh <- &incrRequest{n, result} - return <-result + b.flushedCh <- struct{}{} +} + +func (b *Bar) Incr(n int) { + // result := make(chan bool) + // b.incrRequestCh <- &incrRequest{n, result} + // return <-result + if !b.IsCompleted() { + b.incrCh <- n + } } func (b *Bar) server(wg *sync.WaitGroup) { @@ -187,25 +185,18 @@ var done bool for { select { - case r := <-b.incrRequestCh: - n := completed + r.amount - fmt.Fprintf(os.Stderr, "n = %+v\n", n) + case i := <-b.incrCh: + n := completed + i if n > b.total { - r.result <- false completed = b.total - fmt.Fprintln(os.Stderr, "n > b.total = return false") - break // breaks out of select - } - // r.result <- true - b.updateTimePerItemEstimate(r.amount, blockStartTime) + break + } + b.updateTimePerItemEstimate(i, blockStartTime) completed = n + if completed == b.total && !done { + done = true + } blockStartTime = time.Now() - if completed == b.total && !done { - fmt.Fprintln(os.Stderr, "completed == b.total") - done = true - wg.Done() - } - r.result <- true case d := <-b.decoratorCh: switch d.kind { case decoratorAppend: @@ -214,41 +205,36 @@ prependFuncs = append(prependFuncs, d.f) } case r := <-b.redrawRequestCh: - // fmt.Fprintln(os.Stderr, "redraw") r.bufCh <- b.draw(buf, completed, appendFuncs, prependFuncs) - // case <-b.flushedCh: - // if completed == b.total && !done { - // fmt.Fprintln(os.Stderr, "wg.Done") - // done = true - // wg.Done() - // } else { - // fmt.Fprintln(os.Stderr, "wg.Done not done") - // } + case <-b.flushedCh: + if done && !b.IsCompleted() { + fmt.Fprintln(os.Stderr, "flushedCh: wg.Done") + close(b.done) + wg.Done() + } case <-b.stopCh: fmt.Fprintln(os.Stderr, "received stop signal") - // close(b.incrRequestCh) - close(b.done) if !done { + fmt.Fprintln(os.Stderr, "closing done chan: done = false") + close(b.done) done = true wg.Done() } - // close(b.redrawRequestCh) - // close(b.flushedCh) return } } } func (b *Bar) Stop() { - if !b.IsStopped() { - fmt.Fprintln(os.Stderr, "sending to stopCh") - b.stopCh <- struct{}{} - } else { - fmt.Fprintln(os.Stderr, "Stop: already stopped") - } -} - -func (b *Bar) IsStopped() bool { + b.stopCh <- struct{}{} + // if !b.IsCompleted() { + // fmt.Fprintln(os.Stderr, "sending to stopCh") + // } else { + // fmt.Fprintln(os.Stderr, "Stop: already stopped") + // } +} + +func (b *Bar) IsCompleted() bool { select { case <-b.done: return true diff --git a/example/multi/multi.go b/example/multi/multi.go index 0c98ba9..7df40b6 100644 --- a/example/multi/multi.go +++ b/example/multi/multi.go @@ -1,47 +1,62 @@ package main import ( - "sync" + "fmt" + "math/rand" + "runtime" "time" "github.com/vbauerster/uiprogress" ) +const ( + maxBlockSize = 14 +) + func main() { - waitTime := time.Millisecond * 100 - // p := uiprogress.New().RefreshInterval(100 * time.Millisecond) + runtime.GOMAXPROCS(runtime.NumCPU()) + + decor := func(s *uiprogress.Statistics) string { + str := fmt.Sprintf("%d/%d", s.Completed, s.Total) + return fmt.Sprintf("%-7s", str) + } + p := uiprogress.New() - var wg sync.WaitGroup - bar1 := p.AddBar(20).AppendCompleted().PrependElapsed() - wg.Add(1) + bar1 := p.AddBar(50).AppendETA().PrependFunc(decor) go func() { - defer wg.Done() - for bar1.Incr() { - time.Sleep(waitTime) + blockSize := rand.Intn(maxBlockSize) + 1 + for i := 0; i < 50; i++ { + time.Sleep(time.Duration(blockSize) * (50*time.Millisecond + time.Duration(rand.Intn(5*int(time.Millisecond))))) + bar1.Incr(1) + blockSize = rand.Intn(maxBlockSize) + 1 } }() - bar2 := p.AddBar(40).AppendCompleted().PrependElapsed() - wg.Add(1) + bar2 := p.AddBar(100).AppendETA().PrependFunc(decor) go func() { - defer wg.Done() - for bar2.Incr() { - time.Sleep(waitTime) + blockSize := rand.Intn(maxBlockSize) + 1 + for i := 0; i < 100; i++ { + time.Sleep(time.Duration(blockSize) * (50*time.Millisecond + time.Duration(rand.Intn(5*int(time.Millisecond))))) + bar2.Incr(1) + blockSize = rand.Intn(maxBlockSize) + 1 } }() - time.Sleep(time.Second) - bar3 := p.AddBar(80).PrependElapsed().AppendCompleted() - wg.Add(1) + bar3 := p.AddBar(80).AppendETA().PrependFunc(decor) go func() { - defer wg.Done() - for bar3.Incr() { - time.Sleep(waitTime) + blockSize := rand.Intn(maxBlockSize) + 1 + for i := 0; i < 80; i++ { + time.Sleep(time.Duration(blockSize) * (50*time.Millisecond + time.Duration(rand.Intn(5*int(time.Millisecond))))) + bar3.Incr(1) + blockSize = rand.Intn(maxBlockSize) + 1 } }() - wg.Wait() + // time.Sleep(time.Second) + // p.RemoveBar(bar2) + p.Stop() + fmt.Println("stop") // p.AddBar(1) // panic: send on closed channnel } diff --git a/progress.go b/progress.go index 63189da..330c51d 100644 --- a/progress.go +++ b/progress.go @@ -27,7 +27,7 @@ // Width is the width of the progress bars // Width int - lw *uilive.Writer + // lw *uilive.Writer op chan *operation @@ -46,8 +46,8 @@ // New returns a new progress bar with defaults func New() *progress { p := &progress{ - out: os.Stdout, - lw: uilive.New(), + out: os.Stdout, + // lw: uilive.New(), op: make(chan *operation), interval: make(chan time.Duration), wg: new(sync.WaitGroup), @@ -85,12 +85,13 @@ } // Bypass returns a writer which allows non-buffered data to be written to the underlying output -func (p *progress) Bypass() io.Writer { - return p.lw.Bypass() -} +// func (p *progress) Bypass() io.Writer { +// return p.lw.Bypass() +// } // Stop stops listening func (p *progress) Stop() { + fmt.Fprintln(os.Stderr, "p.Stop") p.wg.Wait() close(p.op) } @@ -98,18 +99,18 @@ // server monitors underlying channels and renders any progress bars func (p *progress) server() { t := time.NewTicker(refreshRate * time.Millisecond) - bars := make([]*Bar, 0) - p.lw.Out = p.out + bars := make([]*Bar, 0, 4) + lw := uilive.New() + lw.Out = p.out for { select { case op, ok := <-p.op: if !ok { + fmt.Fprintln(os.Stderr, "Sopping bars") for _, b := range bars { b.Stop() } t.Stop() - p.lw.Stop() - // close(p.interval) return } switch op.kind { @@ -129,12 +130,12 @@ } case <-t.C: for _, b := range bars { - fmt.Fprintln(p.lw, b.String()) + fmt.Fprintln(lw, b.String()) } - p.lw.Flush() - // for _, b := range bars { - // b.flushed() - // } + lw.Flush() + for _, b := range bars { + b.flushed() + } case d := <-p.interval: t.Stop() t = time.NewTicker(d)