-
Notifications
You must be signed in to change notification settings - Fork 0
/
utils.go
120 lines (108 loc) · 2.34 KB
/
utils.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package progress
import (
"sync"
"time"
)
// WaitGroup is a dropin replacement for sync.WaitGroup
// that provides progress tracking.
type WaitGroup struct {
wg *sync.WaitGroup
LongRunningJob
}
func (wg *WaitGroup) init() {
if wg.wg == nil {
wg.wg = &sync.WaitGroup{}
wg.LongRunningJob = NewLongRunningJob()
}
}
func (wg *WaitGroup) Add(delta int) {
wg.init()
if delta > 0 {
wg.AddWork(uint64(delta))
} else {
wg.FinishWork(uint64(-delta))
}
wg.wg.Add(delta)
}
func (wg *WaitGroup) Done() {
wg.Add(-1)
}
func (wg *WaitGroup) Wait() {
wg.init()
wg.wg.Wait()
}
func Logger(to func(format string, v ...any), prefix string, r Reader) {
sugar := Extend(r)
to("%s 0%%", prefix)
for sugar.InProgress() {
to(`%s%3d%% (%.2f/s, %s remaining)`, prefix, int(100*sugar.Percentage()), sugar.PerSecond(), sugar.Remaining())
}
to("%s100%% (%.2f/s, %s remaining)", prefix, sugar.PerSecond(), sugar.Remaining())
}
// countPoller uses polling to create a Reader.
type countPoller struct {
l *sync.RWMutex
c chan struct{}
finished *bool
fin, tot *uint64
}
func (c countPoller) Count() (uint64, uint64) {
c.l.RLock()
defer c.l.RUnlock()
if *c.finished {
return *c.tot + 1, *c.tot + 1
}
return *c.fin, *c.tot + 1
}
func (c countPoller) DoneChan() (chan struct{}, bool) {
c.l.RLock()
defer c.l.RUnlock()
if *c.finished {
return c.c, true
}
return c.c, false
}
var _ Reader = countPoller{}
// NewReaderFromCount returns an implementation of Reader from just a count callback.
// It will regularly call the count function to determine the latest progress count.
// Note that `count` must be safe to call at any time.
// It will be considered complete when the callback returns equal numbers.
func NewReaderFromCount(count func() (uint64, uint64)) countPoller {
fin, tot := count()
c := countPoller{
l: &sync.RWMutex{},
c: make(chan struct{}),
finished: new(bool),
fin: &fin,
tot: &tot,
}
if fin == tot {
*c.finished = true
close(c.c)
return c
}
go func() {
defer close(c.c)
for {
fin, tot := count()
func() {
c.l.Lock()
defer c.l.Unlock()
if *c.fin < fin {
*c.fin = fin
}
if *c.tot < tot {
*c.tot = tot
}
if *c.fin == *c.tot {
*c.finished = true
}
}()
if fin == tot {
return
}
time.Sleep(30 * time.Millisecond)
}
}()
return c
}