stash/pkg/job/progress.go

177 lines
3.8 KiB
Go

package job
import "sync"
// ProgressIndefinite is the special percent value to indicate that the
// percent progress is not known.
const ProgressIndefinite float64 = -1
// Progress is used by JobExec to communicate updates to the job's progress to
// the JobManager.
type Progress struct {
defined bool
processed int
total int
percent float64
currentTasks []*task
mutex sync.Mutex
updater *updater
}
type task struct {
description string
}
func (p *Progress) updated() {
var details []string
for _, t := range p.currentTasks {
details = append(details, t.description)
}
p.updater.updateProgress(p.percent, details)
}
// Indefinite sets the progress to an indefinite amount.
func (p *Progress) Indefinite() {
p.mutex.Lock()
defer p.mutex.Unlock()
p.defined = false
p.total = 0
p.calculatePercent()
}
// Definite notifies that the total is known.
func (p *Progress) Definite() {
p.mutex.Lock()
defer p.mutex.Unlock()
p.defined = true
p.calculatePercent()
}
// SetTotal sets the total number of work units and sets definite to true.
// This is used to calculate the progress percentage.
func (p *Progress) SetTotal(total int) {
p.mutex.Lock()
defer p.mutex.Unlock()
p.total = total
p.defined = true
p.calculatePercent()
}
// AddTotal adds to the total number of work units. This is used to calculate the
// progress percentage.
func (p *Progress) AddTotal(total int) {
p.mutex.Lock()
defer p.mutex.Unlock()
p.total += total
p.calculatePercent()
}
// SetProcessed sets the number of work units completed. This is used to
// calculate the progress percentage.
func (p *Progress) SetProcessed(processed int) {
p.mutex.Lock()
defer p.mutex.Unlock()
p.processed = processed
p.calculatePercent()
}
func (p *Progress) calculatePercent() {
switch {
case !p.defined || p.total <= 0:
p.percent = ProgressIndefinite
case p.processed < 0:
p.percent = 0
default:
p.percent = float64(p.processed) / float64(p.total)
if p.percent > 1 {
p.percent = 1
}
}
p.updated()
}
// SetPercent sets the progress percent directly. This value will be
// overwritten if Indefinite, SetTotal, Increment or SetProcessed is called.
// Constrains the percent value between 0 and 1, inclusive.
func (p *Progress) SetPercent(percent float64) {
p.mutex.Lock()
defer p.mutex.Unlock()
if percent < 0 {
percent = 0
} else if percent > 1 {
percent = 1
}
p.percent = percent
p.updated()
}
// Increment increments the number of processed work units. This is used to calculate the percentage.
// If total is set already, then the number of processed work units will not exceed the total.
func (p *Progress) Increment() {
p.mutex.Lock()
defer p.mutex.Unlock()
if !p.defined || p.total <= 0 || p.processed < p.total {
p.processed++
p.calculatePercent()
}
}
// AddProcessed increments the number of processed work units by the provided
// amount. This is used to calculate the percentage.
func (p *Progress) AddProcessed(v int) {
p.mutex.Lock()
defer p.mutex.Unlock()
newVal := v
if p.defined && p.total > 0 && newVal > p.total {
newVal = p.total
}
p.processed = newVal
p.calculatePercent()
}
func (p *Progress) addTask(t *task) {
p.mutex.Lock()
defer p.mutex.Unlock()
p.currentTasks = append([]*task{t}, p.currentTasks...)
p.updated()
}
func (p *Progress) removeTask(t *task) {
p.mutex.Lock()
defer p.mutex.Unlock()
for i, tt := range p.currentTasks {
if tt == t {
p.currentTasks = append(p.currentTasks[:i], p.currentTasks[i+1:]...)
p.updated()
return
}
}
}
// ExecuteTask executes a task as part of a job. The description is used to
// populate the Details slice in the parent Job.
func (p *Progress) ExecuteTask(description string, fn func()) {
t := &task{
description: description,
}
p.addTask(t)
defer p.removeTask(t)
fn()
}