Codebase list golang-github-vbauerster-mpb / 9e39a2c
methods on *Progress can be called without panic, after p.Stop() Vladimir Bauer 9 years ago
2 changed file(s) with 98 addition(s) and 118 deletion(s). Raw diff Collapse all Expand all
6767 // WaitGroup for internal rendering sync
6868 wg *sync.WaitGroup
6969
70 done chan struct{}
71 userConf chan userConf
72 bCommandCh chan *bCommandData
73 barCountReqCh chan chan int
74 beforeStopCh chan struct{}
70 done chan struct{}
71 userConfCh chan userConf
72 bCommandCh chan *bCommandData
73 barCountCh chan int
74 beforeStopCh chan struct{}
75
76 // follawing is used after (*Progress.done) is closed
77 conf userConf
7578 }
7679
7780 // New creates new Progress instance, which will orchestrate bars rendering
7982 // If you don't plan to cancel, it is safe to feed with nil
8083 func New() *Progress {
8184 p := &Progress{
82 wg: new(sync.WaitGroup),
83 done: make(chan struct{}),
84 userConf: make(chan userConf),
85 bCommandCh: make(chan *bCommandData),
86 barCountReqCh: make(chan chan int),
87 beforeStopCh: make(chan struct{}),
85 wg: new(sync.WaitGroup),
86 done: make(chan struct{}),
87 userConfCh: make(chan userConf),
88 bCommandCh: make(chan *bCommandData),
89 barCountCh: make(chan int),
90 beforeStopCh: make(chan struct{}),
8891 }
8992 go p.server(userConf{
9093 width: pwidth,
9699 }
97100
98101 // WithCancel cancellation via channel.
99 // pancis, if called on stopped Progress instance, i.e after (*Progress).Stop()
100 // or nil channel passed
102 // Pancis, if nil channel is passed.
101103 func (p *Progress) WithCancel(ch <-chan struct{}) *Progress {
102 if isClosed(p.done) {
103 panic(ErrCallAfterStop)
104 }
105104 if ch == nil {
106105 panic("nil cancel channel")
107106 }
108 conf := <-p.userConf
109 conf.cancel = ch
110 p.userConf <- conf
111 return p
112 }
113
114 // SetWidth overrides default (70) width of bar(s).
115 // pancis, if called on stopped Progress instance, i.e after (*Progress).Stop()
107 p.updateConf(func(c *userConf) {
108 c.cancel = ch
109 })
110 return p
111 }
112
113 // SetWidth overrides default (80) width of bar(s).
116114 func (p *Progress) SetWidth(width int) *Progress {
117 if isClosed(p.done) {
118 panic(ErrCallAfterStop)
119 }
120 if width < 0 {
115 if width < 2 {
121116 return p
122117 }
123 conf := <-p.userConf
124 conf.width = width
125 p.userConf <- conf
118 p.updateConf(func(c *userConf) {
119 c.width = width
120 })
126121 return p
127122 }
128123
129124 // SetOut sets underlying writer of progress. Default one is os.Stdout.
130 // pancis, if called on stopped Progress instance, i.e after (*Progress).Stop()
131125 func (p *Progress) SetOut(w io.Writer) *Progress {
132 if isClosed(p.done) {
133 panic(ErrCallAfterStop)
134 }
135126 if w == nil {
136127 return p
137128 }
138 conf := <-p.userConf
139 conf.cw.Flush()
140 conf.cw = cwriter.New(w)
141 p.userConf <- conf
129 p.updateConf(func(c *userConf) {
130 c.cw.Flush()
131 c.cw = cwriter.New(w)
132 })
142133 return p
143134 }
144135
145136 // RefreshRate overrides default (100ms) refresh rate value
146 // pancis, if called on stopped Progress instance, i.e after (*Progress).Stop()
147137 func (p *Progress) RefreshRate(d time.Duration) *Progress {
148 if isClosed(p.done) {
149 panic(ErrCallAfterStop)
150 }
151 conf := <-p.userConf
152 conf.ticker.Stop()
153 rr = d
154 conf.ticker = time.NewTicker(rr)
155 p.userConf <- conf
138 p.updateConf(func(c *userConf) {
139 c.ticker.Stop()
140 c.ticker = time.NewTicker(d)
141 rr = d
142 })
156143 return p
157144 }
158145
159146 // BeforeRenderFunc accepts a func, which gets called before render process.
160 // pancis, if called on stopped Progress instance, i.e after (*Progress).Stop()
161147 func (p *Progress) BeforeRenderFunc(f BeforeRender) *Progress {
162 if isClosed(p.done) {
163 panic(ErrCallAfterStop)
164 }
165 conf := <-p.userConf
166 conf.beforeRender = f
167 p.userConf <- conf
148 p.updateConf(func(c *userConf) {
149 c.beforeRender = f
150 })
168151 return p
169152 }
170153
171154 // AddBar creates a new progress bar and adds to the container.
172 // pancis, if called on stopped Progress instance, i.e after (*Progress).Stop()
173155 func (p *Progress) AddBar(total int64) *Bar {
174156 return p.AddBarWithID(0, total)
175157 }
176158
177159 // AddBarWithID creates a new progress bar and adds to the container.
178 // pancis, if called on stopped Progress instance, i.e after (*Progress).Stop()
179160 func (p *Progress) AddBarWithID(id int, total int64) *Bar {
180 if isClosed(p.done) {
181 panic(ErrCallAfterStop)
182 }
183 conf := <-p.userConf
161 conf := p.getConf()
162 bar := newBar(id, total, p.wg, &conf)
163 p.bCommandCh <- &bCommandData{
164 action: bAdd,
165 bar: bar,
166 }
167 return bar
168 }
169
170 // RemoveBar removes bar at any time.
171 func (p *Progress) RemoveBar(b *Bar) bool {
184172 result := make(chan bool)
185 bar := newBar(id, total, p.wg, &conf)
186 p.bCommandCh <- &bCommandData{bAdd, bar, result}
187 if <-result {
188 p.wg.Add(1)
189 }
190 return bar
191 }
192
193 // RemoveBar removes bar at any time.
194 // Pancis, if called on stopped Progress instance, i.e after (*Progress).Stop()
195 func (p *Progress) RemoveBar(b *Bar) bool {
196 if isClosed(p.done) {
197 panic(ErrCallAfterStop)
198 }
199 result := make(chan bool)
200 p.bCommandCh <- &bCommandData{bRemove, b, result}
201 return <-result
173 select {
174 case p.bCommandCh <- &bCommandData{bRemove, b, result}:
175 return <-result
176 case <-p.done:
177 return false
178 }
202179 }
203180
204181 // BarCount returns bars count in the container.
205 // Pancis if called on stopped Progress instance, i.e after (*Progress).Stop()
206182 func (p *Progress) BarCount() int {
207 if isClosed(p.done) {
208 panic(ErrCallAfterStop)
209 }
210 respCh := make(chan int, 1)
211 p.barCountReqCh <- respCh
212 return <-respCh
183 select {
184 case count := <-p.barCountCh:
185 return count
186 case <-p.done:
187 return 0
188 }
213189 }
214190
215191 // ShutdownNotify means to be notified when main rendering goroutine quits, usualy after p.Stop() call.
216 // pancis, if called on stopped Progress instance, i.e after (*Progress).Stop()
217192 func (p *Progress) ShutdownNotify(ch chan struct{}) *Progress {
218 if isClosed(p.done) {
219 panic(ErrCallAfterStop)
220 }
221 conf := <-p.userConf
222 conf.shutdownNotifier = ch
223 p.userConf <- conf
193 p.updateConf(func(c *userConf) {
194 c.shutdownNotifier = ch
195 })
224196 return p
225197 }
226198
227199 // Format sets custom format for underlying bar(s), default one is "[=>-]".
228 // pancis, if called on stopped Progress instance, i.e after (*Progress).Stop()
229200 func (p *Progress) Format(format string) *Progress {
230 if isClosed(p.done) {
231 panic(ErrCallAfterStop)
232 }
233201 if utf8.RuneCountInString(format) != numFmtRunes {
234202 return p
235203 }
236 conf := <-p.userConf
237 conf.format = format
238 p.userConf <- conf
204 p.updateConf(func(c *userConf) {
205 c.format = format
206 })
239207 return p
240208 }
241209
251219 p.wg.Wait()
252220
253221 p.beforeStopCh <- struct{}{}
254 fmt.Println("signal sent to p.beforeStopCh")
222 // fmt.Println("signal sent to p.beforeStopCh")
223 // wait for p.server to quit
255224 <-p.done
256 fmt.Println("p.done closed")
225 }
226
227 func (p *Progress) getConf() userConf {
228 select {
229 case conf := <-p.userConfCh:
230 return conf
231 case <-p.done:
232 return p.conf
233 }
234 }
235
236 func (p *Progress) updateConf(cb func(c *userConf)) {
237 c := p.getConf()
238 cb(&c)
239 select {
240 case p.userConfCh <- c:
241 case <-p.done:
242 return
243 }
257244 }
258245
259246 // server monitors underlying channels and renders any progress bars
260247 func (p *Progress) server(conf userConf) {
261248
262249 defer func() {
263 // fmt.Println("exiting p.server")
264 // conf.ticker.Stop()
250 p.conf = conf
251 conf.ticker.Stop()
265252 if conf.shutdownNotifier != nil {
266253 close(conf.shutdownNotifier)
267254 }
268 // fmt.Println("about to close(p.done)")
269255 close(p.done)
270256 }()
271257
283269
284270 for {
285271 select {
286 case p.userConf <- conf:
287 case conf = <-p.userConf:
272 case p.userConfCh <- conf:
273 case conf = <-p.userConfCh:
288274 case data := <-p.bCommandCh:
289275 switch data.action {
290276 case bAdd:
291277 bars = append(bars, data.bar)
292 data.result <- true
278 p.wg.Add(1)
293279 case bRemove:
294280 var ok bool
295281 for i, b := range bars {
302288 }
303289 data.result <- ok
304290 }
305 case respCh := <-p.barCountReqCh:
306 respCh <- len(bars)
291 case p.barCountCh <- len(bars):
307292 case <-conf.ticker.C:
308293 if conf.cancel != nil && isClosed(conf.cancel) {
309294 conf.ticker.Stop()
354339 b.Completed()
355340 }
356341 }
357 conf.ticker.Stop()
358342 return
359343 }
360344 }
44 import "context"
55
66 // WithContext cancellation via context.
7 // pancis, if called on stopped Progress instance, i.e after (*Progress).Stop()
8 // or nil context passed
7 // Pancis, if nil context is passed
98 func (p *Progress) WithContext(ctx context.Context) *Progress {
10 if isClosed(p.done) {
11 panic(ErrCallAfterStop)
12 }
139 if ctx == nil {
1410 panic("nil context")
1511 }
16 conf := <-p.userConf
17 conf.cancel = ctx.Done()
18 p.userConf <- conf
12 p.updateConf(func(c *userConf) {
13 c.cancel = ctx.Done()
14 })
1915 return p
2016 }