diff --git a/bar.go b/bar.go index 919109e..a0839c1 100644 --- a/bar.go +++ b/bar.go @@ -1,7 +1,6 @@ package mpb import ( - "context" "io" "sync" "time" @@ -79,7 +78,7 @@ } ) -func newBar(ctx context.Context, wg *sync.WaitGroup, id int, total int64, width int, format string) *Bar { +func newBar(id int, total int64, width int, format string, wg *sync.WaitGroup, cancel <-chan struct{}) *Bar { b := &Bar{ stateReqCh: make(chan chan state, 1), widthCh: make(chan int), @@ -95,7 +94,7 @@ completeReqCh: make(chan struct{}), done: make(chan struct{}), } - go b.server(ctx, wg, id, total, width, format) + go b.server(id, total, width, format, wg, cancel) return b } @@ -260,7 +259,7 @@ return <-ch } -func (b *Bar) server(ctx context.Context, wg *sync.WaitGroup, id int, total int64, width int, format string) { +func (b *Bar) server(id int, total int64, width int, format string, wg *sync.WaitGroup, cancel <-chan struct{}) { var completed bool timeStarted := time.Now() blockStartTime := timeStarted @@ -324,7 +323,7 @@ return case <-b.removeReqCh: return - case <-ctx.Done(): + case <-cancel: return } } diff --git a/progress.go b/progress.go index e815045..4144a8e 100644 --- a/progress.go +++ b/progress.go @@ -1,7 +1,6 @@ package mpb import ( - "context" "errors" "io" "log" @@ -65,7 +64,7 @@ // Progress represents the container that renders Progress bars type Progress struct { // Context for canceling bars rendering - ctx context.Context + // ctx context.Context // WaitGroup for internal rendering sync wg *sync.WaitGroup @@ -79,15 +78,13 @@ barCountReqCh chan chan int brCh chan BeforeRender done chan struct{} + cancel <-chan struct{} } // New creates new Progress instance, which will orchestrate bars rendering // process. It acceepts context.Context, for cancellation. // If you don't plan to cancel, it is safe to feed with nil -func New(ctx context.Context) *Progress { - if ctx == nil { - ctx = context.Background() - } +func New() *Progress { p := &Progress{ width: pwidth, operationCh: make(chan *operation), @@ -97,19 +94,31 @@ brCh: make(chan BeforeRender), done: make(chan struct{}), wg: new(sync.WaitGroup), - ctx: ctx, } go p.server(cwriter.New(os.Stdout), time.NewTicker(rr*time.Millisecond)) return p } +// WithCancel cancellation via channel +func (p *Progress) WithCancel(ch <-chan struct{}) *Progress { + if ch == nil { + panic("nil cancel channel") + } + p2 := new(Progress) + *p2 = *p + p2.cancel = ch + return p2 +} + // SetWidth overrides default (70) width of bar(s) func (p *Progress) SetWidth(n int) *Progress { - if n <= 0 { - return p - } - p.width = n - return p + if n < 0 { + panic("negative width") + } + p2 := new(Progress) + *p2 = *p + p2.width = n + return p2 } // SetOut sets underlying writer of progress. Default is os.Stdout @@ -157,7 +166,7 @@ panic(ErrCallAfterStop) } result := make(chan bool) - bar := newBar(p.ctx, p.wg, id, total, p.width, p.format) + bar := newBar(id, total, p.width, p.format, p.wg, p.cancel) p.operationCh <- &operation{barAdd, bar, result} if <-result { p.wg.Add(1) @@ -197,7 +206,10 @@ return p } -// Stop waits for bars to finish rendering and stops the rendering goroutine +// Stop shutdowns Progress' goroutine +// Should be called only after each bor's work done, i.e. bar has reached its +// 100 %. It is NOT for concelation. Use WithContext or WithCancel for +// cancelation purposes. func (p *Progress) Stop() { p.wg.Wait() if isClosed(p.done) { @@ -305,7 +317,7 @@ case d := <-p.rrChangeReqCh: t.Stop() t = time.NewTicker(d) - case <-p.ctx.Done(): + case <-p.cancel: return } } diff --git a/progress_go1.7.go b/progress_go1.7.go new file mode 100644 index 0000000..158e548 --- /dev/null +++ b/progress_go1.7.go @@ -0,0 +1,16 @@ +//+build go1.7 + +package mpb + +import "context" + +// WithContext cancellation via context +func (p *Progress) WithContext(ctx context.Context) *Progress { + if ctx == nil { + panic("nil context") + } + p2 := new(Progress) + *p2 = *p + p2.cancel = ctx.Done() + return p2 +}