Codebase list golang-github-vbauerster-mpb / 1213fde
user changeable settings, moved to userConf struct Vladimir Bauer 9 years ago
3 changed file(s) with 142 addition(s) and 102 deletion(s). Raw diff Collapse all Expand all
8686 }
8787 )
8888
89 func newBar(id int, total int64, width int, format string, wg *sync.WaitGroup, cancel <-chan struct{}) *Bar {
89 func newBar(id int, total int64, wg *sync.WaitGroup, conf *userConf) *Bar {
9090 b := &Bar{
9191 stateReqCh: make(chan chan state),
9292 widthCh: make(chan int),
102102 completeReqCh: make(chan struct{}),
103103 done: make(chan struct{}),
104104 }
105 go b.server(id, total, width, format, wg, cancel)
105 go b.server(id, total, wg, conf)
106106 return b
107107 }
108108
204204
205205 // GetID returs id of the bar
206206 func (b *Bar) GetID() int {
207 state := b.getState()
208 return state.id
207 return b.getState().id
209208 }
210209
211210 // InProgress returns true, while progress is running
267266 return <-ch
268267 }
269268
270 func (b *Bar) server(id int, total int64, width int, format string, wg *sync.WaitGroup, cancel <-chan struct{}) {
269 func (b *Bar) server(id int, total int64, wg *sync.WaitGroup, conf *userConf) {
271270 var incrStartTime time.Time
272271 barState := state{
273272 id: id,
274 width: width,
275 format: barFmtRunes{'[', '=', '>', '-', ']'},
273 total: total,
274 width: conf.width,
276275 etaAlpha: 0.25,
277 total: total,
278276 }
279277 if total <= 0 {
280278 barState.simpleSpinner = getSpinner()
281279 } else {
282 barState.updateFormat(format)
280 barState.updateFormat(conf.format)
283281 }
284282 defer func() {
285 b.stop(&barState, width)
283 b.stop(&barState, conf.width)
286284 wg.Done()
287285 }()
288286 for {
332330 return
333331 case <-b.removeReqCh:
334332 return
335 case <-cancel:
333 case <-conf.cancel:
336334 return
337335 }
338336 }
373371 }
374372
375373 func (s *state) updateFormat(format string) {
376 if format == "" {
377 return
378 }
379374 for i, n := 0, 0; len(format) > 0; i++ {
380375 s.format[i], n = utf8.DecodeRuneInString(format)
381376 format = format[n:]
2020 // are called after (*Progress).Stop() has been called
2121 var ErrCallAfterStop = errors.New("method call on stopped Progress instance")
2222
23 // default RefreshRate
24 var rr = 100 * time.Millisecond
25
2326 type (
2427 // BeforeRender is a func, which gets called before render process
2528 BeforeRender func([]*Bar)
3538 listen []chan int
3639 result []chan int
3740 }
41
42 // config changeable by user
43 userConf struct {
44 width int
45 format string
46 beforeRender BeforeRender
47 cw *cwriter.Writer
48 ticker *time.Ticker
49
50 shutdownNotifier chan struct{}
51 cancel <-chan struct{}
52 }
3853 )
3954
4055 const (
4358 )
4459
4560 const (
46 // default RefreshRate
47 rr = 100
4861 // default width
49 pwidth = 70
62 pwidth = 80
63 // default format
64 pformat = "[=>-]"
5065 // number of format runes for bar
5166 numFmtRunes = 5
5267 )
5368
5469 // Progress represents the container that renders Progress bars
5570 type Progress struct {
56 // Context for canceling bars rendering
57 // ctx context.Context
5871 // WaitGroup for internal rendering sync
5972 wg *sync.WaitGroup
6073
61 width int
62 format string
63
64 bCommandCh chan *bCommandData
65 rrChangeReqCh chan time.Duration
66 outChangeReqCh chan io.Writer
67 barCountReqCh chan chan int
68 brCh chan BeforeRender
69 done chan struct{}
70 beforeStop chan struct{}
71 cancel <-chan struct{}
74 done chan struct{}
75 userConf chan userConf
76 bCommandCh chan *bCommandData
77 barCountReqCh chan chan int
78 beforeStop chan struct{}
7279 }
7380
7481 // New creates new Progress instance, which will orchestrate bars rendering
7683 // If you don't plan to cancel, it is safe to feed with nil
7784 func New() *Progress {
7885 p := &Progress{
79 width: pwidth,
80 bCommandCh: make(chan *bCommandData),
81 rrChangeReqCh: make(chan time.Duration),
82 outChangeReqCh: make(chan io.Writer),
83 barCountReqCh: make(chan chan int),
84 brCh: make(chan BeforeRender),
85 done: make(chan struct{}),
86 beforeStop: make(chan struct{}),
87 wg: new(sync.WaitGroup),
88 }
89 go p.server()
90 return p
91 }
92
93 // WithCancel cancellation via channel
86 wg: new(sync.WaitGroup),
87 done: make(chan struct{}),
88 userConf: make(chan userConf),
89 bCommandCh: make(chan *bCommandData),
90 barCountReqCh: make(chan chan int),
91 beforeStop: make(chan struct{}),
92 }
93 go p.server(userConf{
94 width: pwidth,
95 format: pformat,
96 cw: cwriter.New(os.Stdout),
97 ticker: time.NewTicker(rr),
98 })
99 return p
100 }
101
102 // WithCancel cancellation via channel.
103 // pancis, if called on stopped Progress instance, i.e after (*Progress).Stop()
104 // or nil channel passed
94105 func (p *Progress) WithCancel(ch <-chan struct{}) *Progress {
106 if isClosed(p.done) {
107 panic(ErrCallAfterStop)
108 }
95109 if ch == nil {
96110 panic("nil cancel channel")
97111 }
98 p2 := new(Progress)
99 *p2 = *p
100 p2.cancel = ch
101 return p2
102 }
103
104 // SetWidth overrides default (70) width of bar(s)
105 func (p *Progress) SetWidth(n int) *Progress {
106 if n < 0 {
107 panic("negative width")
108 }
109 p2 := new(Progress)
110 *p2 = *p
111 p2.width = n
112 return p2
113 }
114
115 // SetOut sets underlying writer of progress. Default is os.Stdout
112 conf := <-p.userConf
113 conf.cancel = ch
114 p.userConf <- conf
115 return p
116 }
117
118 // SetWidth overrides default (70) width of bar(s).
119 // pancis, if called on stopped Progress instance, i.e after (*Progress).Stop()
120 func (p *Progress) SetWidth(width int) *Progress {
121 if isClosed(p.done) {
122 panic(ErrCallAfterStop)
123 }
124 if width < 0 {
125 return p
126 }
127 conf := <-p.userConf
128 conf.width = width
129 p.userConf <- conf
130 return p
131 }
132
133 // SetOut sets underlying writer of progress. Default one is os.Stdout.
116134 // pancis, if called on stopped Progress instance, i.e after (*Progress).Stop()
117135 func (p *Progress) SetOut(w io.Writer) *Progress {
118136 if isClosed(p.done) {
121139 if w == nil {
122140 return p
123141 }
124 p.outChangeReqCh <- w
142 conf := <-p.userConf
143 conf.cw.Flush()
144 conf.cw = cwriter.New(w)
145 p.userConf <- conf
125146 return p
126147 }
127148
131152 if isClosed(p.done) {
132153 panic(ErrCallAfterStop)
133154 }
134 p.rrChangeReqCh <- d
155 conf := <-p.userConf
156 conf.ticker.Stop()
157 rr = d
158 conf.ticker = time.NewTicker(rr)
159 p.userConf <- conf
135160 return p
136161 }
137162
138163 // BeforeRenderFunc accepts a func, which gets called before render process.
164 // pancis, if called on stopped Progress instance, i.e after (*Progress).Stop()
139165 func (p *Progress) BeforeRenderFunc(f BeforeRender) *Progress {
140166 if isClosed(p.done) {
141167 panic(ErrCallAfterStop)
142168 }
143 p.brCh <- f
144 return p
145 }
146
147 // AddBar creates a new progress bar and adds to the container
169 conf := <-p.userConf
170 conf.beforeRender = f
171 p.userConf <- conf
172 return p
173 }
174
175 // AddBar creates a new progress bar and adds to the container.
148176 // pancis, if called on stopped Progress instance, i.e after (*Progress).Stop()
149177 func (p *Progress) AddBar(total int64) *Bar {
150178 return p.AddBarWithID(0, total)
151179 }
152180
153 // AddBarWithID creates a new progress bar and adds to the container
181 // AddBarWithID creates a new progress bar and adds to the container.
154182 // pancis, if called on stopped Progress instance, i.e after (*Progress).Stop()
155183 func (p *Progress) AddBarWithID(id int, total int64) *Bar {
156184 if isClosed(p.done) {
157185 panic(ErrCallAfterStop)
158186 }
187 conf := <-p.userConf
159188 result := make(chan bool)
160 bar := newBar(id, total, p.width, p.format, p.wg, p.cancel)
189 bar := newBar(id, total, p.wg, &conf)
161190 p.bCommandCh <- &bCommandData{bAdd, bar, result}
162191 if <-result {
163192 p.wg.Add(1)
187216 return <-respCh
188217 }
189218
190 // Format sets custom format for underlying bar(s).
191 // The default one is "[=>-]"
219 // ShutdownNotify means to be notified when main rendering goroutine quits, usualy after p.Stop() call.
220 // pancis, if called on stopped Progress instance, i.e after (*Progress).Stop()
221 func (p *Progress) ShutdownNotify(ch chan struct{}) *Progress {
222 if isClosed(p.done) {
223 panic(ErrCallAfterStop)
224 }
225 conf := <-p.userConf
226 conf.shutdownNotifier = ch
227 p.userConf <- conf
228 return p
229 }
230
231 // Format sets custom format for underlying bar(s), default one is "[=>-]".
232 // pancis, if called on stopped Progress instance, i.e after (*Progress).Stop()
192233 func (p *Progress) Format(format string) *Progress {
234 if isClosed(p.done) {
235 panic(ErrCallAfterStop)
236 }
193237 if utf8.RuneCountInString(format) != numFmtRunes {
194238 return p
195239 }
196 p.format = format
240 conf := <-p.userConf
241 conf.format = format
242 p.userConf <- conf
197243 return p
198244 }
199245
211257 }
212258
213259 // server monitors underlying channels and renders any progress bars
214 func (p *Progress) server() {
215 userRR := rr * time.Millisecond
216 t := time.NewTicker(userRR)
260 func (p *Progress) server(conf userConf) {
217261
218262 defer func() {
219 t.Stop()
263 conf.ticker.Stop()
220264 close(p.done)
265 if conf.shutdownNotifier != nil {
266 close(conf.shutdownNotifier)
267 }
221268 }()
222269
223270 recoverFn := func(ch chan []byte) {
230277 close(ch)
231278 }
232279
233 var beforeRender BeforeRender
234 cw := cwriter.New(os.Stdout)
235280 bars := make([]*Bar, 0, 3)
236281
237282 for {
238283 select {
239 case w := <-p.outChangeReqCh:
240 cw.Flush()
241 cw = cwriter.New(w)
284 case p.userConf <- conf:
285 case conf = <-p.userConf:
242286 case data, ok := <-p.bCommandCh:
243287 if !ok {
244288 return
261305 }
262306 case respCh := <-p.barCountReqCh:
263307 respCh <- len(bars)
264 case beforeRender = <-p.brCh:
265 case <-t.C:
308 case <-conf.ticker.C:
266309 numBars := len(bars)
267310
268311 if numBars == 0 {
269312 break
270313 }
271314
272 if beforeRender != nil {
273 beforeRender(bars)
315 if conf.beforeRender != nil {
316 conf.beforeRender(bars)
274317 }
275318
276319 quitWidthSyncCh := make(chan struct{})
277 time.AfterFunc(userRR, func() {
320 time.AfterFunc(rr, func() {
278321 close(quitWidthSyncCh)
279322 })
280323
292335 ch := fanIn(sequence...)
293336
294337 for buf := range ch {
295 cw.Write(buf)
296 }
297
298 cw.Flush()
338 conf.cw.Write(buf)
339 }
340
341 conf.cw.Flush()
299342
300343 for _, b := range bars {
301344 b.flushed()
302345 }
303 case userRR = <-p.rrChangeReqCh:
304 t.Stop()
305 t = time.NewTicker(userRR)
306346 case <-p.beforeStop:
307347 for _, b := range bars {
308348 if b.GetStatistics().Total <= 0 {
309349 b.Completed()
310350 }
311351 }
312 case <-p.cancel:
352 case <-conf.cancel:
313353 return
314354 }
315355 }
77
88 import "context"
99
10 // WithContext cancellation via context
10 // WithContext cancellation via context.
11 // pancis, if called on stopped Progress instance, i.e after (*Progress).Stop()
12 // or nil context passed
1113 func (p *Progress) WithContext(ctx context.Context) *Progress {
14 if isClosed(p.done) {
15 panic(ErrCallAfterStop)
16 }
1217 if ctx == nil {
1318 panic("nil context")
1419 }
15 p2 := new(Progress)
16 *p2 = *p
17 p2.cancel = ctx.Done()
18 return p2
20 conf := <-p.userConf
21 conf.cancel = ctx.Done()
22 p.userConf <- conf
23 return p
1924 }