Codebase list golang-github-vbauerster-mpb / 2338b32
mutex free bar implementation #1 Vladimir Bauer 9 years ago
3 changed file(s) with 122 addition(s) and 142 deletion(s). Raw diff Collapse all Expand all
+87
-141
bar.go less more
00 package uiprogress
11
2 import (
3 "bytes"
4 "errors"
5 "fmt"
6 "sync"
7 "time"
8
9 "github.com/gosuri/uiprogress/util/strutil"
10 )
2 import "bytes"
113
124 var (
135 // Fill is the default character representing completed progress
2921 Width = 70
3022
3123 // ErrMaxCurrentReached is error when trying to set current value that exceeds the total value
32 ErrMaxCurrentReached = errors.New("errors: current value is greater total value")
24 // ErrMaxCurrentReached = errors.New("errors: current value is greater total value")
3325 )
3426
3527 // Bar represents a progress bar
5244 // Empty is the character that represents the empty progress. Default is '-'
5345 Empty byte
5446
55 // TimeStated is time progress began
56 TimeStarted time.Time
57
5847 // Width is the width of the progress bar
5948 Width int
6049
61 // timeElased is the time elapsed for the progress
62 timeElapsed time.Duration
63 current int
50 currentUpdateCh chan int
6451
65 mtx *sync.RWMutex
52 redrawRequestCh chan redrawRequest
6653
67 appendFuncs []DecoratorFunc
68 prependFuncs []DecoratorFunc
54 // appendFuncs []DecoratorFunc
55 // prependFuncs []DecoratorFunc
6956 }
7057
7158 // DecoratorFunc is a function that can be prepended and appended to the progress bar
7360
7461 // NewBar returns a new progress bar
7562 func NewBar(total int) *Bar {
76 return &Bar{
77 Total: total,
78 Width: Width,
79 LeftEnd: LeftEnd,
80 RightEnd: RightEnd,
81 Head: Head,
82 Fill: Fill,
83 Empty: Empty,
63 b := &Bar{
64 Total: total,
65 Width: Width,
66 LeftEnd: LeftEnd,
67 RightEnd: RightEnd,
68 Head: Head,
69 Fill: Fill,
70 Empty: Empty,
71 currentUpdateCh: make(chan int),
72 redrawRequestCh: make(chan redrawRequest),
73 }
74 go b.server()
75 return b
76 }
8477
85 mtx: &sync.RWMutex{},
78 type redrawRequest struct {
79 bufch chan []byte
80 }
81
82 func (b *Bar) server() {
83 var current int
84 // blockStartTime := time.Now()
85 // timePerItemEstimate time.Duration
86 // remainingTime time.Duration
87 for {
88 select {
89 case n := <-b.currentUpdateCh:
90 current += n
91 // blockStartTime = time.Now()
92 case r := <-b.redrawRequestCh:
93 r.bufch <- b.buffer(current)
94 }
8695 }
8796 }
8897
89 // Set the current count of the bar. It returns ErrMaxCurrentReached when trying n exceeds the total value. This is atomic operation and concurancy safe.
90 func (b *Bar) Set(n int) error {
91 b.mtx.Lock()
92 defer b.mtx.Unlock()
93
94 if n > b.Total {
95 return ErrMaxCurrentReached
96 }
97 b.current = n
98 return nil
98 func (b *Bar) Update(n int) {
99 b.currentUpdateCh <- n
99100 }
100101
101 // Incr increments the current value by 1, time elapsed to current time and returns true. It returns false if the cursor has reached or exceeds total value.
102 func (b *Bar) Incr() bool {
103 b.mtx.Lock()
104 defer b.mtx.Unlock()
105
106 n := b.current + 1
107 if n > b.Total {
108 return false
109 }
110 var t time.Time
111 if b.TimeStarted == t {
112 b.TimeStarted = time.Now()
113 }
114 b.timeElapsed = time.Since(b.TimeStarted)
115 b.current = n
116 return true
117 }
118
119 // Current returns the current progress of the bar
120 func (b *Bar) Current() int {
121 b.mtx.RLock()
122 defer b.mtx.RUnlock()
123 return b.current
124 }
125
126 // AppendFunc runs the decorator function and renders the output on the right of the progress bar
127 func (b *Bar) AppendFunc(f DecoratorFunc) *Bar {
128 b.mtx.Lock()
129 defer b.mtx.Unlock()
130 b.appendFuncs = append(b.appendFuncs, f)
131 return b
132 }
133
134 // AppendCompleted appends the completion percent to the progress bar
135 func (b *Bar) AppendCompleted() *Bar {
136 b.AppendFunc(func(b *Bar) string {
137 return b.CompletedPercentString()
138 })
139 return b
140 }
141
142 // AppendElapsed appends the time elapsed the be progress bar
143 func (b *Bar) AppendElapsed() *Bar {
144 b.AppendFunc(func(b *Bar) string {
145 return strutil.PadLeft(b.TimeElapsedString(), 5, ' ')
146 })
147 return b
148 }
149
150 // PrependFunc runs decorator function and render the output left the progress bar
151 func (b *Bar) PrependFunc(f DecoratorFunc) *Bar {
152 b.mtx.Lock()
153 defer b.mtx.Unlock()
154 b.prependFuncs = append(b.prependFuncs, f)
155 return b
156 }
157
158 // PrependCompleted prepends the precent completed to the progress bar
159 func (b *Bar) PrependCompleted() *Bar {
160 b.PrependFunc(func(b *Bar) string {
161 return b.CompletedPercentString()
162 })
163 return b
164 }
165
166 // PrependElapsed prepends the time elapsed to the begining of the bar
167 func (b *Bar) PrependElapsed() *Bar {
168 b.PrependFunc(func(b *Bar) string {
169 return strutil.PadLeft(b.TimeElapsedString(), 5, ' ')
170 })
171 return b
172 }
173
174 // Bytes returns the byte presentation of the progress bar
175 func (b *Bar) Bytes() []byte {
176 completedWidth := int(float64(b.Width) * (b.CompletedPercent() / 100.00))
102 func (b *Bar) buffer(current int) []byte {
103 completedPercent := int(100 * float64(current) / float64(b.Total))
104 completedWidth := completedPercent * b.Width / 100
177105
178106 // add fill and empty bits
179107 var buf bytes.Buffer
194122 pb[0], pb[len(pb)-1] = b.LeftEnd, b.RightEnd
195123
196124 // render append functions to the right of the bar
197 for _, f := range b.appendFuncs {
198 pb = append(pb, ' ')
199 pb = append(pb, []byte(f(b))...)
200 }
125 // for _, f := range b.appendFuncs {
126 // pb = append(pb, ' ')
127 // pb = append(pb, []byte(f(b))...)
128 // }
201129
202130 // render prepend functions to the left of the bar
203 for _, f := range b.prependFuncs {
204 args := []byte(f(b))
205 args = append(args, ' ')
206 pb = append(args, pb...)
207 }
131 // for _, f := range b.prependFuncs {
132 // args := []byte(f(b))
133 // args = append(args, ' ')
134 // pb = append(args, pb...)
135 // }
208136 return pb
209137 }
210138
211139 // String returns the string representation of the bar
212140 func (b *Bar) String() string {
213 return string(b.Bytes())
141 bufch := make(chan []byte)
142 b.redrawRequestCh <- redrawRequest{bufch}
143 return string(<-bufch)
214144 }
215145
216146 // CompletedPercent return the percent completed
217 func (b *Bar) CompletedPercent() float64 {
218 return (float64(b.Current()) / float64(b.Total)) * 100.00
219 }
147 // func (b *Bar) CompletedPercent() int {
148 // return int(100 * float64(b.current) / float64(b.Total))
149 // }
150
151 // AppendFunc runs the decorator function and renders the output on the right of the progress bar
152 // func (b *Bar) AppendFunc(f DecoratorFunc) *Bar {
153 // b.appendFuncs = append(b.appendFuncs, f)
154 // return b
155 // }
156
157 // PrependFunc runs decorator function and render the output left the progress bar
158 // func (b *Bar) PrependFunc(f DecoratorFunc) *Bar {
159 // b.prependFuncs = append(b.prependFuncs, f)
160 // return b
161 // }
162
163 // AppendCompleted appends the completion percent to the progress bar
164 // func (b *Bar) AppendCompleted() *Bar {
165 // b.AppendFunc(func(b *Bar) string {
166 // return b.CompletedPercentString()
167 // })
168 // return b
169 // }
220170
221171 // CompletedPercentString returns the formatted string representation of the completed percent
222 func (b *Bar) CompletedPercentString() string {
223 return fmt.Sprintf("%3.f%%", b.CompletedPercent())
224 }
172 // func (b *Bar) CompletedPercentString() string {
173 // return fmt.Sprintf("%3d%%", b.CompletedPercent())
174 // }
225175
226 // TimeElapsed returns the time elapsed
227 func (b *Bar) TimeElapsed() time.Duration {
228 b.mtx.RLock()
229 defer b.mtx.RUnlock()
230 return b.timeElapsed
231 }
232
233 // TimeElapsedString returns the formatted string represenation of the time elapsed
234 func (b *Bar) TimeElapsedString() string {
235 return strutil.PrettyTime(b.TimeElapsed())
236 }
176 // PrependElapsed prepends the time elapsed to the begining of the bar
177 // func (b *Bar) PrependElapsed() *Bar {
178 // b.PrependFunc(func(b *Bar) string {
179 // return strutil.PadLeft(b.TimeElapsedString(), 5, ' ')
180 // })
181 // return b
182 // }
0 package main
1
2 import (
3 "fmt"
4 "math/rand"
5 "time"
6
7 "github.com/vbauerster/uiprogress"
8 )
9
10 const (
11 totalItem = 1000
12 maxBlockSize = 20
13 )
14
15 func main() {
16 p := uiprogress.New()
17 bar := p.AddBar(totalItem) // Add a new bar
18
19 // optionally, append and prepend completion and elapsed time
20 // bar.AppendCompleted()
21 // bar.PrependElapsed()
22
23 blockSize := rand.Intn(maxBlockSize) + 1
24 for i := 0; i < totalItem; i += blockSize {
25 time.Sleep(time.Duration(blockSize) * (50*time.Millisecond + time.Duration(rand.Intn(5*int(time.Millisecond)))))
26 bar.Update(i)
27 blockSize = rand.Intn(maxBlockSize) + 1
28 }
29
30 fmt.Println("stop")
31
32 p.Stop()
33 }
1515 remove
1616 )
1717
18 const refreshRate = 30
18 const refreshRate = 100
1919
2020 // progress represents the container that renders progress bars
2121 type progress struct {