refactoring: bar render panic recover
Vladimir Bauer
3 years ago
| 16 | 16 | |
| 17 | 17 | // Bar represents a progress bar. |
| 18 | 18 | type Bar struct { |
| 19 | index int // used by heap | |
| 20 | priority int // used by heap | |
| 21 | frameCh chan *renderFrame | |
| 22 | operateState chan func(*bState) | |
| 23 | done chan struct{} | |
| 24 | container *Progress | |
| 25 | bs *bState | |
| 26 | cancel func() | |
| 27 | recoveredPanic interface{} | |
| 19 | index int // used by heap | |
| 20 | priority int // used by heap | |
| 21 | frameCh chan *renderFrame | |
| 22 | operateState chan func(*bState) | |
| 23 | done chan struct{} | |
| 24 | container *Progress | |
| 25 | bs *bState | |
| 26 | cancel func() | |
| 28 | 27 | } |
| 29 | 28 | |
| 30 | 29 | type extenderFunc func(rows []io.Reader, stat decor.Statistics) []io.Reader |
| 41 | 40 | trimSpace bool |
| 42 | 41 | completed bool |
| 43 | 42 | aborted bool |
| 43 | recovered bool | |
| 44 | 44 | triggerComplete bool |
| 45 | 45 | dropOnComplete bool |
| 46 | 46 | noPop bool |
| 62 | 62 | } |
| 63 | 63 | |
| 64 | 64 | type renderFrame struct { |
| 65 | rows []io.Reader | |
| 66 | shutdown int | |
| 65 | rows []io.Reader | |
| 66 | recovered bool | |
| 67 | shutdown int | |
| 67 | 68 | } |
| 68 | 69 | |
| 69 | 70 | func newBar(container *Progress, bs *bState) *Bar { |
| 356 | 357 | } |
| 357 | 358 | |
| 358 | 359 | func (b *Bar) render(tw int) { |
| 359 | select { | |
| 360 | case b.operateState <- func(s *bState) { | |
| 360 | var done bool | |
| 361 | fn := func(s *bState) { | |
| 361 | 362 | var rows []io.Reader |
| 362 | 363 | stat := newStatistics(tw, s) |
| 363 | 364 | defer func() { |
| 364 | 365 | // recovering if user defined decorator panics for example |
| 365 | 366 | if p := recover(); p != nil { |
| 366 | if s.debugOut != nil { | |
| 367 | go func() { | |
| 368 | _, _ = fmt.Fprintln(b.container, p) | |
| 369 | }() | |
| 370 | if out := s.debugOut; out != nil { | |
| 367 | 371 | for _, fn := range []func() (int, error){ |
| 368 | 372 | func() (int, error) { |
| 369 | return fmt.Fprintln(s.debugOut, p) | |
| 373 | return fmt.Fprintln(out, p) | |
| 370 | 374 | }, |
| 371 | 375 | func() (int, error) { |
| 372 | return s.debugOut.Write(debug.Stack()) | |
| 376 | return out.Write(debug.Stack()) | |
| 373 | 377 | }, |
| 374 | 378 | } { |
| 375 | 379 | if _, err := fn(); err != nil { |
| 378 | 382 | } |
| 379 | 383 | } |
| 380 | 384 | s.aborted = !s.completed |
| 381 | s.extender = makePanicExtender(p) | |
| 382 | b.recoveredPanic = p | |
| 383 | } | |
| 384 | if fn := s.extender; fn != nil { | |
| 385 | rows = fn(rows, stat) | |
| 385 | s.recovered = true | |
| 386 | } else if s.extender != nil { | |
| 387 | rows = s.extender(rows, stat) | |
| 386 | 388 | } |
| 387 | 389 | frame := &renderFrame{ |
| 388 | rows: rows, | |
| 389 | } | |
| 390 | if s.completed || s.aborted { | |
| 390 | rows: rows, | |
| 391 | recovered: s.recovered, | |
| 392 | } | |
| 393 | if !done && (s.completed || s.aborted) { | |
| 391 | 394 | b.cancel() |
| 392 | 395 | frame.shutdown++ |
| 393 | 396 | } |
| 394 | 397 | b.frameCh <- frame |
| 395 | 398 | }() |
| 396 | if b.recoveredPanic == nil { | |
| 397 | rows = append(rows, s.draw(stat)) | |
| 398 | } | |
| 399 | }: | |
| 400 | case <-b.done: | |
| 401 | var rows []io.Reader | |
| 402 | s, stat := b.bs, newStatistics(tw, b.bs) | |
| 403 | if b.recoveredPanic == nil { | |
| 404 | rows = append(rows, s.draw(stat)) | |
| 405 | } | |
| 406 | if fn := s.extender; fn != nil { | |
| 407 | rows = fn(rows, stat) | |
| 408 | } | |
| 409 | frame := &renderFrame{ | |
| 410 | rows: rows, | |
| 411 | } | |
| 412 | b.frameCh <- frame | |
| 399 | rows = append(rows, s.draw(stat)) | |
| 400 | } | |
| 401 | select { | |
| 402 | case b.operateState <- fn: | |
| 403 | case <-b.done: | |
| 404 | done = true | |
| 405 | fn(b.bs) | |
| 413 | 406 | } |
| 414 | 407 | } |
| 415 | 408 | |
| 605 | 598 | } |
| 606 | 599 | return d |
| 607 | 600 | } |
| 608 | ||
| 609 | func makePanicExtender(p interface{}) extenderFunc { | |
| 610 | pstr := fmt.Sprint(p) | |
| 611 | return func(rows []io.Reader, stat decor.Statistics) []io.Reader { | |
| 612 | r := io.MultiReader( | |
| 613 | strings.NewReader(runewidth.Truncate(pstr, stat.AvailableWidth, "…")), | |
| 614 | bytes.NewReader([]byte("\n")), | |
| 615 | ) | |
| 616 | return append(rows, r) | |
| 617 | } | |
| 618 | } | |