Codebase list golang-github-vbauerster-mpb / 8a2b713
Pass operation func to update conf Vladimir Bauer 9 years ago
3 changed file(s) with 99 addition(s) and 112 deletion(s). Raw diff Collapse all Expand all
8484 }
8585 )
8686
87 func newBar(id int, total int64, wg *sync.WaitGroup, conf *userConf) *Bar {
87 func newBar(id int, total int64, width int, format string, wg *sync.WaitGroup, cancel <-chan struct{}) *Bar {
8888 b := &Bar{
89 width: conf.width,
89 width: width,
9090 stateCh: make(chan state),
9191 incrCh: make(chan incrReq),
9292 flushedCh: make(chan struct{}),
9494 completeReqCh: make(chan struct{}),
9595 done: make(chan struct{}),
9696 inProgress: make(chan struct{}),
97 cancel: conf.cancel,
97 cancel: cancel,
9898 }
9999
100100 s := state{
101101 id: id,
102102 total: total,
103 width: conf.width,
103 width: width,
104104 etaAlpha: 0.25,
105105 }
106106
107107 if total <= 0 {
108108 s.simpleSpinner = getSpinner()
109109 } else {
110 s.updateFormat(conf.format)
110 s.updateFormat(format)
111111 }
112112
113113 go b.server(wg, s)
1616 type (
1717 // BeforeRender is a func, which gets called before render process
1818 BeforeRender func([]*Bar)
19 barAction uint
20
21 bCommandData struct {
22 action barAction
23 bar *Bar
24 result chan bool
25 }
2619
2720 widthSync struct {
2821 listen []chan int
3326 userConf struct {
3427 width int
3528 format string
29 bars []*Bar
3630 beforeRender BeforeRender
3731 cw *cwriter.Writer
3832 ticker *time.Ticker
4034 shutdownNotifier chan struct{}
4135 cancel <-chan struct{}
4236 }
43 )
44
45 const (
46 bAdd barAction = iota
47 bRemove
4837 )
4938
5039 const (
6150 // WaitGroup for internal rendering sync
6251 wg *sync.WaitGroup
6352
64 done chan struct{}
65 userConfCh chan userConf
66 bCommandCh chan *bCommandData
67 barCountCh chan int
68 stopReqCh chan struct{}
69 uncompletedBarStopReqCh chan struct{}
53 done chan struct{}
54 ops chan func(*userConf)
55 stopReqCh chan struct{}
7056
7157 // following is used after (*Progress.done) is closed
7258 conf userConf
7763 // If you don't plan to cancel, it is safe to feed with nil
7864 func New() *Progress {
7965 p := &Progress{
80 wg: new(sync.WaitGroup),
81 done: make(chan struct{}),
82 userConfCh: make(chan userConf),
83 bCommandCh: make(chan *bCommandData),
84 barCountCh: make(chan int),
85 stopReqCh: make(chan struct{}),
86 uncompletedBarStopReqCh: make(chan struct{}),
66 wg: new(sync.WaitGroup),
67 done: make(chan struct{}),
68 ops: make(chan func(*userConf)),
69 stopReqCh: make(chan struct{}),
8770 }
8871 go p.server(userConf{
72 bars: make([]*Bar, 0, 3),
8973 width: pwidth,
9074 format: pformat,
9175 cw: cwriter.New(os.Stdout),
10084 if ch == nil {
10185 panic("nil cancel channel")
10286 }
103 p.updateConf(func(c *userConf) {
87 return updateConf(p, func(c *userConf) {
10488 c.cancel = ch
10589 })
106 return p
10790 }
10891
10992 // SetWidth overrides default (80) width of bar(s).
11194 if width < 2 {
11295 return p
11396 }
114 p.updateConf(func(c *userConf) {
97 return updateConf(p, func(c *userConf) {
11598 c.width = width
11699 })
117 return p
118100 }
119101
120102 // SetOut sets underlying writer of progress. Default one is os.Stdout.
122104 if w == nil {
123105 return p
124106 }
125 p.updateConf(func(c *userConf) {
107 return updateConf(p, func(c *userConf) {
126108 c.cw.Flush()
127109 c.cw = cwriter.New(w)
128110 })
129 return p
130111 }
131112
132113 // RefreshRate overrides default (100ms) refresh rate value
133114 func (p *Progress) RefreshRate(d time.Duration) *Progress {
134 p.updateConf(func(c *userConf) {
115 rr = d // TODO: don't update global var
116 return updateConf(p, func(c *userConf) {
135117 c.ticker.Stop()
136118 c.ticker = time.NewTicker(d)
137 rr = d
138 })
139 return p
119 })
140120 }
141121
142122 // BeforeRenderFunc accepts a func, which gets called before render process.
143123 func (p *Progress) BeforeRenderFunc(f BeforeRender) *Progress {
144 p.updateConf(func(c *userConf) {
124 return updateConf(p, func(c *userConf) {
145125 c.beforeRender = f
146126 })
147 return p
148127 }
149128
150129 // AddBar creates a new progress bar and adds to the container.
154133
155134 // AddBarWithID creates a new progress bar and adds to the container.
156135 func (p *Progress) AddBarWithID(id int, total int64) *Bar {
157 conf := p.getConf()
158 bar := newBar(id, total, p.wg, &conf)
159 p.bCommandCh <- &bCommandData{
160 action: bAdd,
161 bar: bar,
162 }
163 return bar
136 result := make(chan *Bar, 1)
137 op := func(c *userConf) {
138 bar := newBar(id, total, c.width, c.format, p.wg, c.cancel)
139 c.bars = append(c.bars, bar)
140 p.wg.Add(1)
141 result <- bar
142 }
143 select {
144 case p.ops <- op:
145 return <-result
146 case <-p.done:
147 return nil
148 }
164149 }
165150
166151 // RemoveBar removes bar at any time.
167152 func (p *Progress) RemoveBar(b *Bar) bool {
168 result := make(chan bool)
169 select {
170 case p.bCommandCh <- &bCommandData{bRemove, b, result}:
153 result := make(chan bool, 1)
154 op := func(c *userConf) {
155 var ok bool
156 for i, bar := range c.bars {
157 if bar == b {
158 c.bars = append(c.bars[:i], c.bars[i+1:]...)
159 bar.remove()
160 ok = true
161 break
162 }
163 }
164 result <- ok
165 }
166 select {
167 case p.ops <- op:
171168 return <-result
172169 case <-p.done:
173170 return false
176173
177174 // BarCount returns bars count in the container.
178175 func (p *Progress) BarCount() int {
179 select {
180 case count := <-p.barCountCh:
181 return count
176 result := make(chan int, 1)
177 op := func(c *userConf) {
178 result <- len(c.bars)
179 }
180 select {
181 case p.ops <- op:
182 return <-result
182183 case <-p.done:
183184 return 0
184185 }
186187
187188 // ShutdownNotify means to be notified when main rendering goroutine quits, usualy after p.Stop() call.
188189 func (p *Progress) ShutdownNotify(ch chan struct{}) *Progress {
189 p.updateConf(func(c *userConf) {
190 return updateConf(p, func(c *userConf) {
190191 c.shutdownNotifier = ch
191192 })
192 return p
193193 }
194194
195195 // Format sets custom format for underlying bar(s), default one is "[=>-]".
197197 if utf8.RuneCountInString(format) != numFmtRunes {
198198 return p
199199 }
200 p.updateConf(func(c *userConf) {
200 return updateConf(p, func(c *userConf) {
201201 c.format = format
202202 })
203 return p
204203 }
205204
206205 // Stop shutdowns Progress' goroutine.
213212 return
214213 default:
215214 // complete Total unknown bars
216 p.uncompletedBarStopReqCh <- struct{}{}
215 p.ops <- func(c *userConf) {
216 for _, b := range c.bars {
217 s := b.getState()
218 if !s.completed && !s.aborted {
219 b.Complete()
220 }
221 }
222 }
217223 // wait for all bars to quit
218224 p.wg.Wait()
219225 // stop request
223229 }
224230 }
225231
226 func (p *Progress) getConf() userConf {
227 select {
228 case conf := <-p.userConfCh:
229 return conf
230 case <-p.done:
231 return p.conf
232 }
233 }
234
235 func (p *Progress) updateConf(cb func(*userConf)) {
236 c := p.getConf()
237 cb(&c)
238 select {
239 case p.userConfCh <- c:
240 case <-p.done:
241 return
232 // func (p *Progress) getConf() userConf {
233 // select {
234 // case conf := <-p.userConfCh:
235 // return conf
236 // case <-p.done:
237 // return p.conf
238 // }
239 // }
240
241 // func (p *Progress) updateConf(op func(*userConf)) {
242 // // c := p.getConf()
243 // // cb(&c)
244 // select {
245 // case p.ops <- op:
246 // case <-p.done:
247 // return
248 // }
249 // }
250
251 func updateConf(p *Progress, op func(*userConf)) *Progress {
252 select {
253 case p.ops <- op:
254 return p
255 case <-p.done:
256 return nil
242257 }
243258 }
244259
262277 close(ch)
263278 }
264279
265 bars := make([]*Bar, 0, 3)
266
267280 for {
268281 select {
269 case p.userConfCh <- conf:
270 case conf = <-p.userConfCh:
271 case data := <-p.bCommandCh:
272 switch data.action {
273 case bAdd:
274 bars = append(bars, data.bar)
275 p.wg.Add(1)
276 case bRemove:
277 var ok bool
278 for i, b := range bars {
279 if b == data.bar {
280 bars = append(bars[:i], bars[i+1:]...)
281 ok = true
282 b.remove()
283 break
284 }
285 }
286 data.result <- ok
287 }
288 case p.barCountCh <- len(bars):
282 case op := <-p.ops:
283 op(&conf)
289284 case <-conf.ticker.C:
290285 var notick bool
291286 select {
296291 default:
297292 }
298293
299 numBars := len(bars)
294 numBars := len(conf.bars)
300295 if notick || numBars == 0 {
301296 break
302297 }
303298
304299 if conf.beforeRender != nil {
305 conf.beforeRender(bars)
300 conf.beforeRender(conf.bars)
306301 }
307302
308303 quitWidthSyncCh := make(chan struct{})
310305 close(quitWidthSyncCh)
311306 })
312307
313 b0 := bars[0]
308 b0 := conf.bars[0]
314309 prependWs := newWidthSync(quitWidthSyncCh, numBars, b0.NumOfPrependers())
315310 appendWs := newWidthSync(quitWidthSyncCh, numBars, b0.NumOfAppenders())
316311
317312 width, _, _ := cwriter.GetTermSize()
318313
319314 sequence := make([]<-chan []byte, numBars)
320 for i, b := range bars {
315 for i, b := range conf.bars {
321316 sequence[i] = b.render(recoverFn, width, prependWs, appendWs)
322317 }
323318
329324
330325 conf.cw.Flush()
331326
332 for _, b := range bars {
327 for _, b := range conf.bars {
333328 b.flushed()
334 }
335 case <-p.uncompletedBarStopReqCh:
336 for _, b := range bars {
337 s := b.getState()
338 if !s.completed && !s.aborted {
339 b.Complete()
340 }
341329 }
342330 case <-p.stopReqCh:
343331 return
99 if ctx == nil {
1010 panic("nil context")
1111 }
12 p.updateConf(func(c *userConf) {
12 return updateConf(p, func(c *userConf) {
1313 c.cancel = ctx.Done()
1414 })
15 return p
1615 }