/
pool.go
102 lines (87 loc) · 1.92 KB
/
pool.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
package thermocline
import (
"fmt"
"sync"
"github.com/bradfitz/iter"
)
// A Pool is a set of workers that all function on the same queue
type Pool struct {
Queue string
Version string
read <-chan *Task
write chan<- *Task
fn Processor
// workers are only kept track of by their stop channel
workers []chan struct{}
wg *sync.WaitGroup
*sync.RWMutex
}
// NewPool returns a running worker pool on the given queue/version
func NewPool(queue, version string, b Broker, fn Processor, workers int) (*Pool, error) {
r, err := b.Read(queue, version)
if err != nil {
return nil, err
}
w, err := b.Write(queue, version)
if err != nil {
return nil, err
}
p := &Pool{
Queue: queue,
Version: version,
read: r,
write: w,
fn: fn,
workers: make([]chan struct{}, 0),
wg: &sync.WaitGroup{},
RWMutex: &sync.RWMutex{},
}
err = p.Add(workers)
if err != nil {
return nil, err
}
return p, nil
}
// Add changes the number of workers
func (p *Pool) Add(n int) error {
if n >= 1 {
for range iter.N(n) {
stopC := make(chan struct{})
p.wg.Add(1)
go NewWorker(p.read, p.write, p.fn, stopC).Work(p.wg)
p.Lock()
p.workers = append(p.workers, stopC)
p.Unlock()
}
} else if n <= -1 {
for range iter.N(-1 * n) {
p.Lock()
// close channel to stop worker
close(p.workers[len(p.workers)-1])
// from github.com/golang/go/wiki/SliceTricks
// Delete without preserving order
p.workers[len(p.workers)-1] = nil
p.workers = p.workers[:len(p.workers)-1]
// unlock
p.Unlock()
}
} else {
return fmt.Errorf("%d is not a valid number of workers to add", n)
}
return nil
}
// Len returns the total number of workers in this group
func (p *Pool) Len() int {
p.RLock()
defer p.RUnlock()
return len(p.workers)
}
// Stop turns off all workers in the pool
func (p *Pool) Stop() error {
err := p.Add(-1 * p.Len())
if err != nil {
return err
}
p.wg.Wait()
return nil
}