From 19290114fc022f7ff8e08338411c885251d206dd Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Mon, 18 Feb 2019 18:57:29 -0500 Subject: fi-progress: ratelimit to 60fps --- fi-progress/progress.go | 146 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 98 insertions(+), 48 deletions(-) (limited to 'fi-progress') diff --git a/fi-progress/progress.go b/fi-progress/progress.go index a49923f..5d8a070 100644 --- a/fi-progress/progress.go +++ b/fi-progress/progress.go @@ -16,9 +16,12 @@ package main import ( + "context" "fmt" "os" "regexp" + "sync/atomic" + "time" "git.lukeshu.com/go/libfastimport" @@ -38,7 +41,7 @@ func main() { frontend := libfastimport.NewFrontend(os.Stdin, os.Stdin, nil) backend := libfastimport.NewBackend(os.Stdout, os.Stdout, nil) - filter := &Progress{ + filter := &Filter{ backend: backend, } for _, str := range os.Args[1:] { @@ -51,51 +54,98 @@ func main() { } filter.filters = append(filter.filters, re) } + filter.writer = NewWriter(len(filter.filters)) - filter.start() + ctx, ctxCancel := context.WithCancel(context.Background()) + done := make(chan struct{}) + go func() { + filter.writer.Run(ctx) + done <- struct{}{} + }() err := fiutil.RunHandler(frontend, filter) - filter.end() + ctxCancel() + <-done if err != nil { fmt.Fprintf(os.Stderr, "%s: error: %v\n", os.Args[0], err) os.Exit(1) } } -type Progress struct { - backend *libfastimport.Backend - filters []*regexp.Regexp +func NewWriter(n int) *Writer { + ret := &Writer{ + lines: make([]atomic.Value, n), + written: make([]string, n), + } + for i := range ret.lines { + ret.lines[i].Store("progress ") + } + return ret +} + +type Writer struct { + lines []atomic.Value + written []string curLine int } -func (h *Progress) start() { - for _ = range h.filters { - fmt.Fprintln(os.Stderr, "progress ") - h.curLine++ +func (w *Writer) Write(n int, str string) { + w.lines[n].Store(str) +} + +func (w *Writer) flush() { + for i := range w.lines { + newline := w.lines[i].Load().(string) + if newline == w.written[i] { + continue + } + d := i - w.curLine + var prefix string + switch { + case d < 0: + prefix = fmt.Sprintf("\x1B[%dA", -d) + case d == 0: + prefix = "" + case d > 0: + prefix = fmt.Sprintf("\x1B[%dB", d) + } + fmt.Fprintf(os.Stderr, "\r%s%s\x1B[0K", prefix, newline) + w.written[i] = newline + w.curLine = i } } -func (h *Progress) end() { - if h.curLine == len(h.filters) { - return +func (w *Writer) Run(ctx context.Context) { + for i := range w.lines { + w.written[i] = w.lines[i].Load().(string) + fmt.Fprintln(os.Stderr, w.written[i]) + w.curLine++ + } + ticker := time.NewTicker(time.Second / 60) + for { + select { + case <-ctx.Done(): + ticker.Stop() + w.flush() + if w.curLine != len(w.lines) { + fmt.Fprintf(os.Stderr, "\r\x1B[%dB", len(w.lines)-w.curLine) + } + return + case <-ticker.C: + w.flush() + } } - fmt.Fprintf(os.Stderr, "\r\x1B[%dB", len(h.filters)-h.curLine) } -func (h *Progress) CmdProgress(cmd libfastimport.CmdProgress) error { +type Filter struct { + backend *libfastimport.Backend + filters []*regexp.Regexp + writer *Writer +} + +func (h *Filter) CmdProgress(cmd libfastimport.CmdProgress) error { for i := range h.filters { if h.filters[i].MatchString(cmd.Str) { - d := i - h.curLine - var prefix string - switch { - case d < 0: - prefix = fmt.Sprintf("\x1B[%dA", -d) - case d == 0: - prefix = "" - case d > 0: - prefix = fmt.Sprintf("\x1B[%dB", d) - } - fmt.Fprintf(os.Stderr, "\r%sprogress %s\x1B[0K", prefix, cmd.Str) - h.curLine = i + h.writer.Write(i, "progress "+cmd.Str) return nil } } @@ -103,35 +153,35 @@ func (h *Progress) CmdProgress(cmd libfastimport.CmdProgress) error { } // pass through everything else -func (h *Progress) CmdBlob(cmd libfastimport.CmdBlob) error { return h.backend.Do(cmd) } -func (h *Progress) CmdCheckpoint(cmd libfastimport.CmdCheckpoint) error { return h.backend.Do(cmd) } -func (h *Progress) CmdComment(cmd libfastimport.CmdComment) error { return h.backend.Do(cmd) } -func (h *Progress) CmdCommit(cmd libfastimport.CmdCommit) error { return h.backend.Do(cmd) } -func (h *Progress) CmdCommitEnd(cmd libfastimport.CmdCommitEnd) error { return h.backend.Do(cmd) } -func (h *Progress) CmdDone(cmd libfastimport.CmdDone) error { return h.backend.Do(cmd) } -func (h *Progress) CmdFeature(cmd libfastimport.CmdFeature) error { return h.backend.Do(cmd) } -func (h *Progress) CmdOption(cmd libfastimport.CmdOption) error { return h.backend.Do(cmd) } -func (h *Progress) CmdReset(cmd libfastimport.CmdReset) error { return h.backend.Do(cmd) } -func (h *Progress) CmdTag(cmd libfastimport.CmdTag) error { return h.backend.Do(cmd) } -func (h *Progress) FileCopy(cmd libfastimport.FileCopy) error { return h.backend.Do(cmd) } -func (h *Progress) FileDelete(cmd libfastimport.FileDelete) error { return h.backend.Do(cmd) } -func (h *Progress) FileDeleteAll(cmd libfastimport.FileDeleteAll) error { return h.backend.Do(cmd) } -func (h *Progress) FileModify(cmd libfastimport.FileModify) error { return h.backend.Do(cmd) } -func (h *Progress) FileModifyInline(cmd libfastimport.FileModifyInline) error { +func (h *Filter) CmdBlob(cmd libfastimport.CmdBlob) error { return h.backend.Do(cmd) } +func (h *Filter) CmdCheckpoint(cmd libfastimport.CmdCheckpoint) error { return h.backend.Do(cmd) } +func (h *Filter) CmdComment(cmd libfastimport.CmdComment) error { return h.backend.Do(cmd) } +func (h *Filter) CmdCommit(cmd libfastimport.CmdCommit) error { return h.backend.Do(cmd) } +func (h *Filter) CmdCommitEnd(cmd libfastimport.CmdCommitEnd) error { return h.backend.Do(cmd) } +func (h *Filter) CmdDone(cmd libfastimport.CmdDone) error { return h.backend.Do(cmd) } +func (h *Filter) CmdFeature(cmd libfastimport.CmdFeature) error { return h.backend.Do(cmd) } +func (h *Filter) CmdOption(cmd libfastimport.CmdOption) error { return h.backend.Do(cmd) } +func (h *Filter) CmdReset(cmd libfastimport.CmdReset) error { return h.backend.Do(cmd) } +func (h *Filter) CmdTag(cmd libfastimport.CmdTag) error { return h.backend.Do(cmd) } +func (h *Filter) FileCopy(cmd libfastimport.FileCopy) error { return h.backend.Do(cmd) } +func (h *Filter) FileDelete(cmd libfastimport.FileDelete) error { return h.backend.Do(cmd) } +func (h *Filter) FileDeleteAll(cmd libfastimport.FileDeleteAll) error { return h.backend.Do(cmd) } +func (h *Filter) FileModify(cmd libfastimport.FileModify) error { return h.backend.Do(cmd) } +func (h *Filter) FileModifyInline(cmd libfastimport.FileModifyInline) error { return h.backend.Do(cmd) } -func (h *Progress) FileRename(cmd libfastimport.FileRename) error { return h.backend.Do(cmd) } -func (h *Progress) NoteModify(cmd libfastimport.NoteModify) error { return h.backend.Do(cmd) } -func (h *Progress) NoteModifyInline(cmd libfastimport.NoteModifyInline) error { +func (h *Filter) FileRename(cmd libfastimport.FileRename) error { return h.backend.Do(cmd) } +func (h *Filter) NoteModify(cmd libfastimport.NoteModify) error { return h.backend.Do(cmd) } +func (h *Filter) NoteModifyInline(cmd libfastimport.NoteModifyInline) error { return h.backend.Do(cmd) } -func (h *Progress) CmdCatBlob(cmd libfastimport.CmdCatBlob) (sha1 string, data string, err error) { +func (h *Filter) CmdCatBlob(cmd libfastimport.CmdCatBlob) (sha1 string, data string, err error) { return h.backend.CatBlob(cmd) } -func (h *Progress) CmdGetMark(cmd libfastimport.CmdGetMark) (sha1 string, err error) { +func (h *Filter) CmdGetMark(cmd libfastimport.CmdGetMark) (sha1 string, err error) { return h.backend.GetMark(cmd) } -func (h *Progress) CmdLs(cmd libfastimport.CmdLs) (mode libfastimport.Mode, dataref string, path libfastimport.Path, err error) { +func (h *Filter) CmdLs(cmd libfastimport.CmdLs) (mode libfastimport.Mode, dataref string, path libfastimport.Path, err error) { return h.backend.Ls(cmd) } -- cgit v1.2.2