Imported Upstream version 2.4.0
[scm/test.git] / tasklog / percentage_task.go
1 package tasklog
2
3 import (
4         "fmt"
5         "math"
6         "sync/atomic"
7         "time"
8 )
9
10 // PercentageTask is a task that is performed against a known number of
11 // elements.
12 type PercentageTask struct {
13         // members managed via sync/atomic must be aligned at the top of this
14         // structure (see: https://github.com/git-lfs/git-lfs/pull/2880).
15
16         // n is the number of elements whose work has been completed. It is
17         // managed sync/atomic.
18         n uint64
19         // total is the total number of elements to execute work upon.
20         total uint64
21         // msg is the task message.
22         msg string
23         // ch is a channel which is written to when the task state changes and
24         // is closed when the task is completed.
25         ch chan *Update
26 }
27
28 func NewPercentageTask(msg string, total uint64) *PercentageTask {
29         p := &PercentageTask{
30                 msg:   msg,
31                 total: total,
32                 ch:    make(chan *Update, 1),
33         }
34         p.Count(0)
35
36         return p
37 }
38
39 // Count indicates that work has been completed against "n" number of elements,
40 // marking the task as complete if the total "n" given to all invocations of
41 // this method is equal to total.
42 //
43 // Count returns the new total number of (atomically managed) elements that have
44 // been completed.
45 func (c *PercentageTask) Count(n uint64) (new uint64) {
46         if new = atomic.AddUint64(&c.n, n); new > c.total {
47                 panic("tasklog: counted too many items")
48         }
49
50         var percentage float64
51         if c.total == 0 {
52                 percentage = 100
53         } else {
54                 percentage = 100 * float64(new) / float64(c.total)
55         }
56
57         c.ch <- &Update{
58                 S: fmt.Sprintf("%s: %3.f%% (%d/%d)",
59                         c.msg, math.Floor(percentage), new, c.total),
60                 At: time.Now(),
61         }
62
63         if new >= c.total {
64                 close(c.ch)
65         }
66
67         return new
68 }
69
70 // Entry logs a line-delimited task entry.
71 func (t *PercentageTask) Entry(update string) {
72         t.ch <- &Update{
73                 S:     fmt.Sprintf("%s\n", update),
74                 At:    time.Now(),
75                 Force: true,
76         }
77 }
78
79 // Updates implements Task.Updates and returns a channel which is written to
80 // when the state of this task changes, and closed when the task is completed.
81 // has been completed.
82 func (c *PercentageTask) Updates() <-chan *Update {
83         return c.ch
84 }
85
86 // Throttled implements Task.Throttled and returns true, indicating that this
87 // task is throttled.
88 func (c *PercentageTask) Throttled() bool { return true }