diff --git a/bar.go b/bar.go index d64e8c5..84682c8 100644 --- a/bar.go +++ b/bar.go @@ -33,10 +33,11 @@ index int operateState chan func(*bState) - quit chan struct{} + done chan struct{} + shutdown chan struct{} once sync.Once - // cacheState is used after quit is closed + // cacheState is used after done is closed cacheState *bState } @@ -97,7 +98,8 @@ b := &Bar{ priority: id, operateState: make(chan func(*bState)), - quit: make(chan struct{}), + done: make(chan struct{}), + shutdown: make(chan struct{}), } go b.serve(s, wg, cancel) @@ -110,7 +112,7 @@ case b.operateState <- func(s *bState) { s.prependFuncs = nil }: - case <-b.quit: + case <-b.done: } } @@ -120,7 +122,7 @@ case b.operateState <- func(s *bState) { s.appendFuncs = nil }: - case <-b.quit: + case <-b.done: } } @@ -166,7 +168,7 @@ s.completed = true } }: - case <-b.quit: + case <-b.done: } } @@ -180,7 +182,7 @@ case b.operateState <- func(s *bState) { s.refill = &refill{r, till} }: - case <-b.quit: + case <-b.done: } } @@ -190,7 +192,7 @@ select { case b.operateState <- func(s *bState) { result <- len(s.appendFuncs) }: return <-result - case <-b.quit: + case <-b.done: return len(b.cacheState.appendFuncs) } } @@ -201,7 +203,7 @@ select { case b.operateState <- func(s *bState) { result <- len(s.prependFuncs) }: return <-result - case <-b.quit: + case <-b.done: return len(b.cacheState.prependFuncs) } } @@ -212,7 +214,7 @@ select { case b.operateState <- func(s *bState) { result <- s.id }: return <-result - case <-b.quit: + case <-b.done: return b.cacheState.id } } @@ -223,7 +225,7 @@ select { case b.operateState <- func(s *bState) { result <- s.current }: return <-result - case <-b.quit: + case <-b.done: return b.cacheState.current } } @@ -234,7 +236,7 @@ select { case b.operateState <- func(s *bState) { result <- s.total }: return <-result - case <-b.quit: + case <-b.done: return b.cacheState.total } } @@ -247,7 +249,7 @@ s.total = total s.dynamic = !final }: - case <-b.quit: + case <-b.done: } } @@ -255,7 +257,7 @@ // Can be used as condition in for loop func (b *Bar) InProgress() bool { select { - case <-b.quit: + case <-b.done: return false default: return true @@ -265,27 +267,25 @@ // Complete stops bar's progress tracking, but not removes the bar. // If you need to remove, call Progress.RemoveBar(*Bar) instead. func (b *Bar) Complete() { - b.once.Do(b.shutdown) - <-b.quit -} - -func (b *Bar) shutdown() { - b.quit <- struct{}{} + b.once.Do(func() { + close(b.shutdown) + }) } func (b *Bar) serve(s *bState, wg *sync.WaitGroup, cancel <-chan struct{}) { + defer func() { + b.cacheState = s + close(b.done) + wg.Done() + }() for { select { case op := <-b.operateState: op(s) case <-cancel: s.aborted = true - cancel = nil - go b.Complete() - case <-b.quit: - b.cacheState = s - close(b.quit) - wg.Done() + return + case <-b.shutdown: return } } @@ -311,7 +311,7 @@ s.draw(tw, prependWs, appendWs) ch <- &bufReader{io.MultiReader(s.bufP, s.bufB, s.bufA), s.completed} }: - case <-b.quit: + case <-b.done: s := b.cacheState var r io.Reader if s.panic != "" { diff --git a/progress.go b/progress.go index 3597b0b..bf9d931 100644 --- a/progress.go +++ b/progress.go @@ -27,8 +27,11 @@ ewg *sync.WaitGroup operateState chan func(*pState) - quit chan struct{} + done chan struct{} + shutdown chan struct{} once sync.Once + + cacheHeap *priorityQueue } type ( @@ -81,7 +84,8 @@ ewg: s.ewg, wg: new(sync.WaitGroup), operateState: make(chan func(*pState)), - quit: make(chan struct{}), + done: make(chan struct{}), + shutdown: make(chan struct{}), } go p.serve(s) return p @@ -101,8 +105,9 @@ result <- b }: return <-result - case <-p.quit: - return new(Bar) + case <-p.done: + // fail early + return nil } } @@ -120,7 +125,7 @@ } }: return <-result - case <-p.quit: + case <-p.done: return false } } @@ -132,7 +137,7 @@ case p.operateState <- func(s *pState) { s.bHeap.update(b, priority) }: - case <-p.quit: + case <-p.done: } } @@ -144,8 +149,8 @@ result <- s.bHeap.Len() }: return <-result - case <-p.quit: - return 0 + case <-p.done: + return p.cacheHeap.Len() } } @@ -159,12 +164,10 @@ } // first wait for all bars to quit p.wg.Wait() - p.once.Do(p.shutdown) - <-p.quit -} - -func (p *Progress) shutdown() { - p.quit <- struct{}{} + p.once.Do(func() { + close(p.shutdown) + }) + <-p.done } func newWidthSync(timeout <-chan struct{}, numBars, numColumn int) *widthSync { diff --git a/progress_posix.go b/progress_posix.go index bfa2eef..be5bafd 100644 --- a/progress_posix.go +++ b/progress_posix.go @@ -17,10 +17,20 @@ winch := make(chan os.Signal, 1) signal.Notify(winch, syscall.SIGWINCH) + defer func() { + s.ticker.Stop() + signal.Stop(winch) + p.cacheHeap = s.bHeap + close(p.done) + if s.shutdownNotifier != nil { + close(s.shutdownNotifier) + } + }() + var numP, numA int var timer *time.Timer var resumeTicker <-chan time.Time - resumeDelay := 320 * time.Millisecond + resumeDelay := 300 * time.Millisecond for { select { @@ -57,18 +67,8 @@ s.ticker = time.NewTicker(s.rr) resumeTicker = nil case <-s.cancel: - s.ticker.Stop() - s.cancel = nil - // don't return here, p.Stop() must be called eventually - case <-p.quit: - close(p.quit) - if s.cancel != nil { - s.ticker.Stop() - } - if s.shutdownNotifier != nil { - close(s.shutdownNotifier) - } - signal.Stop(winch) + return + case <-p.shutdown: return } } diff --git a/progress_test.go b/progress_test.go index 6168520..a078879 100644 --- a/progress_test.go +++ b/progress_test.go @@ -13,6 +13,10 @@ "github.com/vbauerster/mpb" "github.com/vbauerster/mpb/decor" ) + +func init() { + rand.Seed(time.Now().UnixNano()) +} func TestAddBar(t *testing.T) { p := mpb.New() diff --git a/progress_windows.go b/progress_windows.go index f9a18ba..dd45a81 100644 --- a/progress_windows.go +++ b/progress_windows.go @@ -11,6 +11,15 @@ ) func (p *Progress) serve(s *pState) { + + defer func() { + s.ticker.Stop() + p.cacheHeap = s.bHeap + close(p.done) + if s.shutdownNotifier != nil { + close(s.shutdownNotifier) + } + }() var numP, numA int @@ -34,17 +43,8 @@ fmt.Fprintln(os.Stderr, err) } case <-s.cancel: - s.ticker.Stop() - s.cancel = nil - // don't return here, p.Stop() must be called eventually - case <-p.quit: - close(p.quit) - if s.cancel != nil { - s.ticker.Stop() - } - if s.shutdownNotifier != nil { - close(s.shutdownNotifier) - } + return + case <-p.shutdown: return } }