diff --git a/bar.go b/bar.go index f8d2a6e..995056d 100644 --- a/bar.go +++ b/bar.go @@ -32,20 +32,24 @@ // Bar represents a progress Bar. type Bar struct { - priority int - index int - - cacheState *bState - operateState chan func(*bState) - bFrameCh chan *bFrame - syncTableCh chan [][]chan int - completed chan bool - forceRefresh chan<- time.Time - - // done is closed by Bar's goroutine, after cacheState is written + priority int // used by heap + index int // used by heap + + extendedLines int + toShutdown bool + dropOnComplete bool + operateState chan func(*bState) + frameCh chan io.Reader + syncTableCh chan [][]chan int + completed chan bool + forceRefresh chan<- time.Time + + // shutdown is closed when bar completed and flushed + shutdown chan struct{} + // done is closed after cacheState is written done chan struct{} - // shutdown is closed from master Progress goroutine only - shutdown chan struct{} + // cacheState is populated, right after close(shutdown) + cacheState *bState arbitraryCurrent struct { sync.Mutex @@ -55,47 +59,34 @@ dlogger *log.Logger } -type ( - bState struct { - filler Filler - extender Filler - id int - width int - total int64 - current int64 - trimSpace bool - toComplete bool - removeOnComplete bool - barClearOnComplete bool - completeFlushed bool - aDecorators []decor.Decorator - pDecorators []decor.Decorator - amountReceivers []decor.AmountReceiver - shutdownListeners []decor.ShutdownListener - bufP, bufB, bufA *bytes.Buffer - bufE *bytes.Buffer - panicMsg string - - // priority overrides *Bar's priority, if set - priority int - // runningBar is a key for *pState.parkedBars - runningBar *Bar - } - bFrame struct { - rd io.Reader - extendedLines int - toShutdown bool - removeOnComplete bool - } -) - -func newBar( - ctx context.Context, - wg *sync.WaitGroup, - forceRefresh chan<- time.Time, - bs *bState, - dlogger *log.Logger, -) *Bar { +type bState struct { + filler Filler + extender Filler + id int + width int + total int64 + current int64 + trimSpace bool + toComplete bool + completeFlushed bool + noBufBOnComplete bool + aDecorators []decor.Decorator + pDecorators []decor.Decorator + amountReceivers []decor.AmountReceiver + shutdownListeners []decor.ShutdownListener + bufP, bufB, bufA *bytes.Buffer + bufE *bytes.Buffer + panicMsg string + + // priority overrides *Bar's priority, if set + priority int + // dropOnComplete propagates to *Bar + dropOnComplete bool + // runningBar is a key for *pState.parkedBars + runningBar *Bar +} + +func newBar(ctx context.Context, wg *sync.WaitGroup, bs *bState) *Bar { bs.bufP = bytes.NewBuffer(make([]byte, 0, bs.width)) bs.bufB = bytes.NewBuffer(make([]byte, 0, bs.width)) @@ -105,15 +96,14 @@ } bar := &Bar{ - priority: bs.priority, - operateState: make(chan func(*bState)), - bFrameCh: make(chan *bFrame, 1), - syncTableCh: make(chan [][]chan int), - completed: make(chan bool), - done: make(chan struct{}), - shutdown: make(chan struct{}), - forceRefresh: forceRefresh, - dlogger: dlogger, + priority: bs.priority, + dropOnComplete: bs.dropOnComplete, + operateState: make(chan func(*bState)), + frameCh: make(chan io.Reader, 1), + syncTableCh: make(chan [][]chan int), + completed: make(chan bool), + done: make(chan struct{}), + shutdown: make(chan struct{}), } go bar.serve(ctx, wg, bs) @@ -283,45 +273,45 @@ defer func() { // recovering if user defined decorator panics for example if p := recover(); p != nil { + b.dlogger.Println(p) s.panicMsg = fmt.Sprintf("panic: %v", p) - b.dlogger.Println(s.panicMsg) - b.bFrameCh <- &bFrame{ - rd: strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%ds\n", tw), s.panicMsg)), - toShutdown: true, - } + close(b.shutdown) + b.frameCh <- s.drawPanic(tw) } }() - frame := &bFrame{ - rd: s.draw(tw), - toShutdown: s.toComplete && !s.completeFlushed, - removeOnComplete: s.removeOnComplete, - } + + frame := s.draw(tw) + b.toShutdown = s.toComplete && !s.completeFlushed + s.completeFlushed = s.toComplete + if s.extender != nil { s.extender.Fill(s.bufE, tw, newStatistics(s)) - frame.extendedLines = countLines(s.bufE.Bytes()) - frame.rd = io.MultiReader(frame.rd, s.bufE) - } - b.bFrameCh <- frame - s.completeFlushed = s.toComplete + b.extendedLines = countLines(s.bufE.Bytes()) + frame = io.MultiReader(frame, s.bufE) + } + b.frameCh <- frame }: case <-b.done: s := b.cacheState - frame := &bFrame{ - rd: s.draw(tw), - } + if s.panicMsg != "" { + b.frameCh <- s.drawPanic(tw) + return + } + frame := s.draw(tw) if s.extender != nil { s.extender.Fill(s.bufE, tw, newStatistics(s)) - frame.extendedLines = countLines(s.bufE.Bytes()) - frame.rd = io.MultiReader(frame.rd, s.bufE) - } - b.bFrameCh <- frame - } + b.extendedLines = countLines(s.bufE.Bytes()) + frame = io.MultiReader(frame, s.bufE) + } + b.frameCh <- frame + } +} + +func (s *bState) drawPanic(termWidth int) io.Reader { + return strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%ds\n", termWidth), s.panicMsg)) } func (s *bState) draw(termWidth int) io.Reader { - if s.panicMsg != "" { - return strings.NewReader(fmt.Sprintf(fmt.Sprintf("%%.%ds\n", termWidth), s.panicMsg)) - } stat := newStatistics(s) @@ -333,7 +323,7 @@ s.bufA.WriteString(d.Decor(stat)) } - if s.barClearOnComplete && s.completeFlushed { + if s.noBufBOnComplete && s.completeFlushed { s.bufA.WriteByte('\n') return io.MultiReader(s.bufP, s.bufA) } diff --git a/bar_option.go b/bar_option.go index 180db6b..2cf07f3 100644 --- a/bar_option.go +++ b/bar_option.go @@ -57,7 +57,7 @@ // gets removed completely. func BarRemoveOnComplete() BarOption { return func(s *bState) { - s.removeOnComplete = true + s.dropOnComplete = true } } @@ -82,7 +82,7 @@ // BarRemoveOnComplete. func BarClearOnComplete() BarOption { return func(s *bState) { - s.barClearOnComplete = true + s.noBufBOnComplete = true } } diff --git a/bar_test.go b/bar_test.go index fe5fbbc..fd56bf4 100644 --- a/bar_test.go +++ b/bar_test.go @@ -132,7 +132,6 @@ p.Wait() - wantPanic = fmt.Sprintf("panic: %s", wantPanic) debugStr := buf.String() if !strings.Contains(debugStr, wantPanic) { t.Errorf("%q doesn't contain %q\n", debugStr, wantPanic) diff --git a/progress.go b/progress.go index 4daa338..cea0c84 100644 --- a/progress.go +++ b/progress.go @@ -133,9 +133,10 @@ opt(bs) } } + bar := newBar(p.ctx, p.bwg, bs) + bar.forceRefresh = p.forceRefresh prefix := fmt.Sprintf("%sbar#%02d ", p.dlogger.Prefix(), bs.id) - dlogger := log.New(ps.debugOut, prefix, log.Lshortfile) - bar := newBar(p.ctx, p.bwg, p.forceRefresh, bs, dlogger) + bar.dlogger = log.New(ps.debugOut, prefix, log.Lshortfile) if bs.runningBar != nil { if bar.priority == ps.idCounter { bar.priority = bs.runningBar.priority @@ -262,9 +263,9 @@ var lineCount int for s.bHeap.Len() > 0 { bar := heap.Pop(s.bHeap).(*Bar) - frame := <-bar.bFrameCh + frame := <-bar.frameCh defer func() { - if frame.toShutdown { + if bar.toShutdown { // shutdown at next flush, in other words decrement underlying WaitGroup // only after the bar with completed state has been flushed. this // ensures no bar ends up with less than 100% rendered. @@ -274,15 +275,15 @@ s.heapUpdated = true delete(s.parkedBars, bar) } - if frame.removeOnComplete { + if bar.dropOnComplete { s.heapUpdated = true return } } heap.Push(s.bHeap, bar) }() - cw.ReadFrom(frame.rd) - lineCount += frame.extendedLines + 1 + cw.ReadFrom(frame) + lineCount += bar.extendedLines + 1 } for i := len(s.shutdownPending) - 1; i >= 0; i-- {