1// +build linux darwin freebsd netbsd openbsd solaris dragonfly windows plan9
2
3package pb
4
5import (
6	"io"
7	"sync"
8	"time"
9)
10
11// Create and start new pool with given bars
12// You need call pool.Stop() after work
13func StartPool(pbs ...*ProgressBar) (pool *Pool, err error) {
14	pool = new(Pool)
15	if err = pool.Start(); err != nil {
16		return
17	}
18	pool.Add(pbs...)
19	return
20}
21
22// NewPool initialises a pool with progress bars, but
23// doesn't start it. You need to call Start manually
24func NewPool(pbs ...*ProgressBar) (pool *Pool) {
25	pool = new(Pool)
26	pool.Add(pbs...)
27	return
28}
29
30type Pool struct {
31	Output        io.Writer
32	RefreshRate   time.Duration
33	bars          []*ProgressBar
34	lastBarsCount int
35	shutdownCh    chan struct{}
36	workerCh      chan struct{}
37	m             sync.Mutex
38	finishOnce    sync.Once
39}
40
41// Add progress bars.
42func (p *Pool) Add(pbs ...*ProgressBar) {
43	p.m.Lock()
44	defer p.m.Unlock()
45	for _, bar := range pbs {
46		bar.ManualUpdate = true
47		bar.NotPrint = true
48		bar.Start()
49		p.bars = append(p.bars, bar)
50	}
51}
52
53func (p *Pool) Start() (err error) {
54	p.RefreshRate = DefaultRefreshRate
55	p.shutdownCh, err = lockEcho()
56	if err != nil {
57		return
58	}
59	p.workerCh = make(chan struct{})
60	go p.writer()
61	return
62}
63
64func (p *Pool) writer() {
65	var first = true
66	defer func() {
67		if first == false {
68			p.print(false)
69		} else {
70			p.print(true)
71			p.print(false)
72		}
73		close(p.workerCh)
74	}()
75
76	for {
77		select {
78		case <-time.After(p.RefreshRate):
79			if p.print(first) {
80				p.print(false)
81				return
82			}
83			first = false
84		case <-p.shutdownCh:
85			return
86		}
87	}
88}
89
90// Restore terminal state and close pool
91func (p *Pool) Stop() error {
92	p.finishOnce.Do(func() {
93		if p.shutdownCh != nil {
94			close(p.shutdownCh)
95		}
96	})
97
98	// Wait for the worker to complete
99	select {
100	case <-p.workerCh:
101	}
102
103	return unlockEcho()
104}
105