Shutdown does one operation, refactoring and more tests
Vladimir Bauer
8 years ago
| 261 | 261 | } |
| 262 | 262 | } |
| 263 | 263 | |
| 264 | // Complete signals to the bar, that process has been completed. | |
| 265 | // You should call this method when total is unknown and you've reached the point | |
| 266 | // of process completion. If you don't call this method, it will be called | |
| 267 | // implicitly, upon p.Stop() call. | |
| 264 | // Complete stops bar's progress tracking, but not removes the bar. | |
| 265 | // If you need to remove, call Progress.RemoveBar(*Bar) instead. | |
| 268 | 266 | func (b *Bar) Complete() { |
| 269 | 267 | b.once.Do(b.shutdown) |
| 268 | <-b.quit | |
| 270 | 269 | } |
| 271 | 270 | |
| 272 | 271 | func (b *Bar) shutdown() { |
| 273 | 272 | b.quit <- struct{}{} |
| 274 | <-b.quit | |
| 275 | 273 | } |
| 276 | 274 | |
| 277 | 275 | func (b *Bar) serve(s *bState, wg *sync.WaitGroup, cancel <-chan struct{}) { |
| 278 | defer wg.Done() | |
| 279 | ||
| 280 | 276 | for { |
| 281 | 277 | select { |
| 282 | 278 | case op := <-b.operateState: |
| 288 | 284 | case <-b.quit: |
| 289 | 285 | b.cacheState = s |
| 290 | 286 | close(b.quit) |
| 287 | wg.Done() | |
| 291 | 288 | return |
| 292 | 289 | } |
| 293 | 290 | } |
| 41 | 41 | } |
| 42 | 42 | } |
| 43 | 43 | |
| 44 | // WithRefreshRate overrides default 100ms refresh rate | |
| 44 | // WithRefreshRate overrides default 120ms refresh rate | |
| 45 | 45 | func WithRefreshRate(d time.Duration) ProgressOption { |
| 46 | 46 | return func(s *pState) { |
| 47 | if d < 10*time.Millisecond { | |
| 48 | return | |
| 49 | } | |
| 47 | 50 | s.ticker.Stop() |
| 48 | 51 | s.ticker = time.NewTicker(d) |
| 49 | 52 | s.rr = d |
| 156 | 156 | if p.ewg != nil { |
| 157 | 157 | p.ewg.Wait() |
| 158 | 158 | } |
| 159 | // wait for all bars to quit | |
| 159 | // first wait for all bars to quit | |
| 160 | 160 | p.wg.Wait() |
| 161 | 161 | p.once.Do(p.shutdown) |
| 162 | <-p.quit | |
| 162 | 163 | } |
| 163 | 164 | |
| 164 | 165 | func (p *Progress) shutdown() { |
| 165 | 166 | p.quit <- struct{}{} |
| 166 | <-p.quit | |
| 167 | 167 | } |
| 168 | 168 | |
| 169 | 169 | func newWidthSync(timeout <-chan struct{}, numBars, numColumn int) *widthSync { |
| 205 | 205 | |
| 206 | 206 | func (s *pState) writeAndFlush(tw, numP, numA int) (err error) { |
| 207 | 207 | wSyncTimeout := make(chan struct{}) |
| 208 | time.AfterFunc(s.rr, func() { | |
| 208 | time.AfterFunc(s.rr-s.rr/12, func() { | |
| 209 | 209 | close(wSyncTimeout) |
| 210 | 210 | }) |
| 211 | 211 | |
| 60 | 60 | s.cancel = nil |
| 61 | 61 | // don't return here, p.Stop() must be called eventually |
| 62 | 62 | case <-p.quit: |
| 63 | close(p.quit) | |
| 63 | 64 | if s.cancel != nil { |
| 64 | 65 | s.ticker.Stop() |
| 65 | 66 | } |
| 67 | 68 | close(s.shutdownNotifier) |
| 68 | 69 | } |
| 69 | 70 | signal.Stop(winch) |
| 70 | close(p.quit) | |
| 71 | 71 | return |
| 72 | 72 | } |
| 73 | 73 | } |
| 50 | 50 | p.Stop() |
| 51 | 51 | } |
| 52 | 52 | |
| 53 | func TestRemoveBars(t *testing.T) { | |
| 54 | p := mpb.New(mpb.Output(ioutil.Discard)) | |
| 55 | ||
| 56 | var wg sync.WaitGroup | |
| 57 | bars := make([]*mpb.Bar, 3) | |
| 58 | for i := 0; i < 3; i++ { | |
| 59 | wg.Add(1) | |
| 60 | b := p.AddBar(80) | |
| 61 | bars[i] = b | |
| 62 | go func() { | |
| 63 | for i := 0; i < 80; i++ { | |
| 64 | if i == 33 { | |
| 65 | wg.Done() | |
| 66 | } | |
| 67 | b.Increment() | |
| 68 | time.Sleep(randomDuration(80 * time.Millisecond)) | |
| 69 | } | |
| 70 | }() | |
| 71 | } | |
| 72 | ||
| 73 | wg.Wait() | |
| 74 | for i := 0; i < 3; i++ { | |
| 75 | i := i | |
| 76 | go func() { | |
| 77 | if ok := p.RemoveBar(bars[i]); !ok { | |
| 78 | t.Errorf("bar %d: remove failed\n", i) | |
| 79 | } | |
| 80 | }() | |
| 81 | } | |
| 82 | p.Stop() | |
| 83 | } | |
| 84 | ||
| 53 | 85 | func TestWithCancel(t *testing.T) { |
| 54 | 86 | var wg sync.WaitGroup |
| 55 | 87 | cancel := make(chan struct{}) |
| 78 | 110 | return |
| 79 | 111 | default: |
| 80 | 112 | } |
| 81 | time.Sleep(time.Duration(rand.Intn(10)+1) * time.Second / 100) | |
| 113 | time.Sleep(randomDuration(80 * time.Millisecond)) | |
| 82 | 114 | bar.Increment() |
| 83 | 115 | } |
| 84 | 116 | }() |
| 106 | 138 | mpb.WithCancel(cancel), |
| 107 | 139 | mpb.WithFormat(customFormat), |
| 108 | 140 | ) |
| 109 | bar := p.AddBar(100, mpb.BarTrim()) | |
| 141 | bar := p.AddBar(80, mpb.BarTrim()) | |
| 110 | 142 | |
| 143 | var wg sync.WaitGroup | |
| 144 | wg.Add(1) | |
| 111 | 145 | go func() { |
| 112 | for i := 0; i < 100; i++ { | |
| 113 | time.Sleep(10 * time.Millisecond) | |
| 146 | for i := 0; i < 80; i++ { | |
| 147 | if i == 33 { | |
| 148 | wg.Done() | |
| 149 | } | |
| 150 | time.Sleep(randomDuration(80 * time.Millisecond)) | |
| 114 | 151 | bar.Increment() |
| 115 | 152 | } |
| 116 | 153 | }() |
| 117 | 154 | |
| 118 | time.Sleep(300 * time.Millisecond) | |
| 155 | wg.Wait() | |
| 119 | 156 | close(cancel) |
| 120 | 157 | p.Stop() |
| 121 | 158 | |
| 138 | 175 | bar := p.AddBar(100, mpb.BarTrim()) |
| 139 | 176 | |
| 140 | 177 | for i := 0; i < 100; i++ { |
| 141 | time.Sleep(10 * time.Millisecond) | |
| 178 | time.Sleep(randomDuration(40 * time.Millisecond)) | |
| 142 | 179 | bar.Increment() |
| 143 | 180 | } |
| 144 | 181 | |
| 150 | 187 | t.Errorf("Expected format: %s, got %s\n", want, got) |
| 151 | 188 | } |
| 152 | 189 | } |
| 190 | ||
| 191 | func randomDuration(max time.Duration) time.Duration { | |
| 192 | return time.Duration(rand.Intn(10)+1) * max / 10 | |
| 193 | } | |