mirror of
https://github.com/restic/restic.git
synced 2026-08-30 09:17:14 +00:00
Split Scanner from Archiver, refactor Progress
This commit is contained in:
+165
@@ -0,0 +1,165 @@
|
||||
package restic
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Progress struct {
|
||||
F ProgressFunc
|
||||
D ProgressFunc
|
||||
fnM sync.Mutex
|
||||
|
||||
cur Stat
|
||||
curM sync.Mutex
|
||||
start time.Time
|
||||
c *time.Ticker
|
||||
cancel chan struct{}
|
||||
o sync.Once
|
||||
d time.Duration
|
||||
|
||||
running bool
|
||||
}
|
||||
|
||||
type Stat struct {
|
||||
Files uint64
|
||||
Dirs uint64
|
||||
Other uint64
|
||||
Bytes uint64
|
||||
}
|
||||
|
||||
type ProgressFunc func(s Stat, runtime time.Duration, ticker bool)
|
||||
|
||||
// NewProgress returns a new progress reporter. After Start() has been called,
|
||||
// the function fn is called when new data arrives or at least every d
|
||||
// interval. The function doneFn is called when Done() is called. Both
|
||||
// functions F and D are called synchronously and can use shared state.
|
||||
func NewProgress(d time.Duration) *Progress {
|
||||
return &Progress{d: d}
|
||||
}
|
||||
|
||||
// Start runs resets and runs the progress reporter.
|
||||
func (p *Progress) Start() {
|
||||
if p == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if p.running {
|
||||
panic("truing to reset a running Progress")
|
||||
}
|
||||
|
||||
p.o = sync.Once{}
|
||||
p.cancel = make(chan struct{})
|
||||
p.running = true
|
||||
p.Reset()
|
||||
p.start = time.Now()
|
||||
p.c = time.NewTicker(p.d)
|
||||
|
||||
go p.reporter()
|
||||
}
|
||||
|
||||
// Report adds the statistics from s to the current state and tries to report
|
||||
// the accumulated statistics via the feedback channel.
|
||||
func (p *Progress) Report(s Stat) {
|
||||
if p == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !p.running {
|
||||
panic("reporting in a non-running Progress")
|
||||
}
|
||||
|
||||
p.curM.Lock()
|
||||
p.cur.Add(s)
|
||||
cur := p.cur
|
||||
p.curM.Unlock()
|
||||
|
||||
// update progress
|
||||
if p.F != nil {
|
||||
p.fnM.Lock()
|
||||
p.F(cur, time.Since(p.start), false)
|
||||
p.fnM.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Progress) reporter() {
|
||||
if p == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-p.c.C:
|
||||
p.curM.Lock()
|
||||
cur := p.cur
|
||||
p.curM.Unlock()
|
||||
|
||||
if p.F != nil {
|
||||
p.fnM.Lock()
|
||||
p.F(cur, time.Since(p.start), true)
|
||||
p.fnM.Unlock()
|
||||
}
|
||||
case <-p.cancel:
|
||||
p.c.Stop()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reset resets all statistic counters to zero.
|
||||
func (p *Progress) Reset() {
|
||||
if p == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !p.running {
|
||||
panic("resetting a non-running Progress")
|
||||
}
|
||||
|
||||
p.curM.Lock()
|
||||
p.cur = Stat{}
|
||||
p.curM.Unlock()
|
||||
}
|
||||
|
||||
// Done closes the progress report.
|
||||
func (p *Progress) Done() {
|
||||
if p == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !p.running {
|
||||
panic("Done() called on non-running Progress")
|
||||
}
|
||||
|
||||
if p.running {
|
||||
p.running = false
|
||||
p.o.Do(func() {
|
||||
close(p.cancel)
|
||||
})
|
||||
|
||||
cur := p.cur
|
||||
|
||||
if p.D != nil {
|
||||
p.fnM.Lock()
|
||||
p.D(cur, time.Since(p.start), false)
|
||||
p.fnM.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Current returns the current stat value.
|
||||
func (p *Progress) Current() Stat {
|
||||
p.curM.Lock()
|
||||
s := p.cur
|
||||
p.curM.Unlock()
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// Add accumulates other into s.
|
||||
func (s *Stat) Add(other Stat) {
|
||||
s.Bytes += other.Bytes
|
||||
s.Dirs += other.Dirs
|
||||
s.Files += other.Files
|
||||
s.Other += other.Other
|
||||
}
|
||||
Reference in New Issue
Block a user