util/progresstracking: add Ticker, NewWriter, and CountingWriter
Add three new helpers to the existing progresstracking package:
- Ticker: spawns a 1 Hz goroutine that calls a report function with
the current value of an atomic counter and a total. Returns a stop
function (safe to call multiple times via sync.OnceFunc) that fires
one final report and blocks until the goroutine exits.
- NewWriter: wraps an io.Writer and calls onProgress at most once per
interval with the cumulative byte count.
- CountingWriter: an io.Writer that atomically counts bytes written,
for use with Ticker.
These will be used by the appliance flash and OTA update code in
subsequent commits.
Updates #1866
Change-Id: If353cea6506f5351b6fb19bfdb7bc9b78fe7855e
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
committed by
Brad Fitzpatrick
parent
d0fcb668d5
commit
a8f3c861a4
@@ -18,14 +18,13 @@ import (
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/bradfitz/monogok/disklayout"
|
||||
"github.com/peterbourgon/ff/v3/ffcli"
|
||||
"tailscale.com/clientupdate"
|
||||
"tailscale.com/clientupdate/distsign"
|
||||
"tailscale.com/gokrazy/mkfs"
|
||||
"tailscale.com/util/progresstracking"
|
||||
"tailscale.com/util/prompt"
|
||||
)
|
||||
|
||||
@@ -442,62 +441,19 @@ func writeZipMemberAt(f *os.File, zf *zip.File, offset int64) error {
|
||||
return err
|
||||
}
|
||||
total := int64(zf.UncompressedSize64)
|
||||
cw := &countingWriter{w: f}
|
||||
stop := startProgress(zf.Name, total, &cw.count)
|
||||
cw := &progresstracking.CountingWriter{W: f}
|
||||
stop := progresstracking.Ticker(cw.Count, total, func(d, t int64) {
|
||||
pct := 0.0
|
||||
if t > 0 {
|
||||
pct = float64(d) * 100 / float64(t)
|
||||
}
|
||||
fmt.Fprintf(Stderr, " %s: %s / %s (%.1f%%)\n", zf.Name, humanBytes(d), humanBytes(t), pct)
|
||||
})
|
||||
defer stop()
|
||||
_, err = io.Copy(cw, rc)
|
||||
return err
|
||||
}
|
||||
|
||||
// countingWriter wraps an io.Writer and tracks total bytes written so
|
||||
// the progress goroutine can report it.
|
||||
type countingWriter struct {
|
||||
w io.Writer
|
||||
count atomic.Int64
|
||||
}
|
||||
|
||||
func (c *countingWriter) Write(b []byte) (int, error) {
|
||||
n, err := c.w.Write(b)
|
||||
if n > 0 {
|
||||
c.count.Add(int64(n))
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
|
||||
// startProgress spawns a 1 Hz goroutine that prints "<name>: <done> / <total>"
|
||||
// to Stderr until the returned stop function is called. The final tick on
|
||||
// stop reports the final state, so the caller doesn't need to repeat it.
|
||||
func startProgress(name string, total int64, done *atomic.Int64) func() {
|
||||
stop := make(chan struct{})
|
||||
finished := make(chan struct{})
|
||||
go func() {
|
||||
defer close(finished)
|
||||
t := time.NewTicker(time.Second)
|
||||
defer t.Stop()
|
||||
report := func() {
|
||||
d := done.Load()
|
||||
pct := 0.0
|
||||
if total > 0 {
|
||||
pct = float64(d) * 100 / float64(total)
|
||||
}
|
||||
fmt.Fprintf(Stderr, " %s: %s / %s (%.1f%%)\n", name, humanBytes(d), humanBytes(total), pct)
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case <-stop:
|
||||
report()
|
||||
return
|
||||
case <-t.C:
|
||||
report()
|
||||
}
|
||||
}
|
||||
}()
|
||||
return func() {
|
||||
close(stop)
|
||||
<-finished
|
||||
}
|
||||
}
|
||||
|
||||
// humanBytes returns a friendly approximation of n bytes, e.g. "62.5 GB".
|
||||
func humanBytes(n int64) string {
|
||||
const (
|
||||
|
||||
@@ -292,6 +292,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
|
||||
tailscale.com/util/mak from tailscale.com/cmd/tailscale/cli+
|
||||
tailscale.com/util/must from tailscale.com/clientupdate/distsign+
|
||||
tailscale.com/util/nocasemaps from tailscale.com/types/ipproto
|
||||
tailscale.com/util/progresstracking from tailscale.com/clientupdate/distsign+
|
||||
tailscale.com/util/prompt from tailscale.com/cmd/tailscale/cli
|
||||
💣 tailscale.com/util/qrcodes from tailscale.com/cmd/tailscale/cli
|
||||
tailscale.com/util/quarantine from tailscale.com/cmd/tailscale/cli
|
||||
|
||||
@@ -469,7 +469,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
||||
W 💣 tailscale.com/util/osdiag/internal/wsc from tailscale.com/util/osdiag
|
||||
tailscale.com/util/osshare from tailscale.com/cmd/tailscaled+
|
||||
tailscale.com/util/osuser from tailscale.com/ipn/ipnlocal+
|
||||
tailscale.com/util/progresstracking from tailscale.com/feature/taildrop
|
||||
tailscale.com/util/progresstracking from tailscale.com/feature/taildrop+
|
||||
tailscale.com/util/race from tailscale.com/net/dns/resolver
|
||||
tailscale.com/util/racebuild from tailscale.com/logpolicy
|
||||
tailscale.com/util/rands from tailscale.com/ipn/ipnlocal+
|
||||
|
||||
Reference in New Issue
Block a user