| 22 | 22 |
|
| 23 | 23 |
// Progress represents a container that renders one or more progress bars.
|
| 24 | 24 |
type Progress struct {
|
| 25 | |
ctx context.Context
|
| 26 | 25 |
uwg *sync.WaitGroup
|
| 27 | 26 |
bwg *sync.WaitGroup
|
| 28 | 27 |
operateState chan func(*pState)
|
|
| 34 | 33 |
|
| 35 | 34 |
// pState holds bars in its priorityQueue, it gets passed to (*Progress).serve monitor goroutine.
|
| 36 | 35 |
type pState struct {
|
|
36 |
ctx context.Context
|
| 37 | 37 |
hm heapManager
|
| 38 | 38 |
rows []io.Reader
|
| 39 | 39 |
|
|
| 64 | 64 |
// context. It's not possible to reuse instance after (*Progress).Wait
|
| 65 | 65 |
// method has been called.
|
| 66 | 66 |
func NewWithContext(ctx context.Context, options ...ContainerOption) *Progress {
|
|
67 |
ctx, cancel := context.WithCancel(ctx)
|
| 67 | 68 |
s := &pState{
|
|
69 |
ctx: ctx,
|
| 68 | 70 |
hm: make(heapManager),
|
| 69 | 71 |
rows: make([]io.Reader, 32),
|
| 70 | 72 |
refreshRate: defaultRefreshRate,
|
|
| 84 | 86 |
s.manualRefresh = make(chan interface{})
|
| 85 | 87 |
}
|
| 86 | 88 |
|
| 87 | |
ctx, cancel := context.WithCancel(ctx)
|
| 88 | 89 |
p := &Progress{
|
| 89 | |
ctx: ctx,
|
| 90 | 90 |
uwg: s.uwg,
|
| 91 | 91 |
bwg: new(sync.WaitGroup),
|
| 92 | 92 |
operateState: make(chan func(*pState)),
|
|
| 95 | 95 |
shutdown: make(chan struct{}),
|
| 96 | 96 |
cancel: cancel,
|
| 97 | 97 |
}
|
| 98 | |
|
| 99 | |
go p.serve(s, cwriter.New(s.output))
|
|
98 |
cw := cwriter.New(s.output)
|
|
99 |
go p.serve(s, cw, s.newTicker(cw.IsTerminal(), p.done))
|
|
100 |
go s.hm.run()
|
| 100 | 101 |
return p
|
| 101 | 102 |
}
|
| 102 | 103 |
|
|
| 130 | 131 |
select {
|
| 131 | 132 |
case p.operateState <- func(ps *pState) {
|
| 132 | 133 |
bs := ps.makeBarState(total, filler, options...)
|
| 133 | |
bar := newBar(p, bs)
|
|
134 |
bar := newBar(ps.ctx, p, bs)
|
| 134 | 135 |
if bs.wait.bar != nil {
|
| 135 | 136 |
ps.queueBars[bs.wait.bar] = bar
|
| 136 | 137 |
} else {
|
|
| 229 | 230 |
<-p.shutdown
|
| 230 | 231 |
}
|
| 231 | 232 |
|
| 232 | |
func (p *Progress) serve(s *pState, cw *cwriter.Writer) {
|
|
233 |
func (p *Progress) serve(s *pState, cw *cwriter.Writer, tickerC <-chan time.Time) {
|
| 233 | 234 |
var err error
|
| 234 | 235 |
render := func() error { return s.render(cw) }
|
| 235 | |
tickerC := s.newTicker(p.ctx, cw.IsTerminal(), p.done)
|
| 236 | |
|
| 237 | |
go s.hm.run()
|
| 238 | 236 |
|
| 239 | 237 |
for {
|
| 240 | 238 |
select {
|
|
| 269 | 267 |
}
|
| 270 | 268 |
}
|
| 271 | 269 |
|
| 272 | |
func (s *pState) newTicker(ctx context.Context, isTerminal bool, done chan struct{}) chan time.Time {
|
|
270 |
func (s *pState) newTicker(isTerminal bool, done chan struct{}) chan time.Time {
|
| 273 | 271 |
ch := make(chan time.Time, 1)
|
| 274 | 272 |
go func() {
|
| 275 | 273 |
var autoRefresh <-chan time.Time
|
|
| 291 | 289 |
} else {
|
| 292 | 290 |
ch <- time.Now()
|
| 293 | 291 |
}
|
| 294 | |
case <-ctx.Done():
|
|
292 |
case <-s.ctx.Done():
|
| 295 | 293 |
close(done)
|
| 296 | 294 |
return
|
| 297 | 295 |
}
|