summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2019-02-18 18:57:29 -0500
committerLuke Shumaker <lukeshu@lukeshu.com>2019-02-18 18:57:29 -0500
commit19290114fc022f7ff8e08338411c885251d206dd (patch)
tree5a4aef1e479bc78ed849a8c502d22a9b8c143e96
parent019f0dfd8d8b3a77552a39ddf83d0f4fc6e8b2f8 (diff)
fi-progress: ratelimit to 60fps
-rw-r--r--fi-progress/progress.go146
1 files changed, 98 insertions, 48 deletions
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)
}