2020-10-25 17:00:21 +01:00
|
|
|
package pool
|
|
|
|
|
2020-10-30 21:08:43 +01:00
|
|
|
import (
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/deluan/navidrome/log"
|
|
|
|
)
|
|
|
|
|
2020-10-25 17:00:21 +01:00
|
|
|
type Executor func(workload interface{})
|
|
|
|
|
|
|
|
type Pool struct {
|
2020-10-27 20:43:00 +01:00
|
|
|
name string
|
|
|
|
item interface{}
|
|
|
|
workers []worker
|
|
|
|
exec Executor
|
2020-10-30 21:08:43 +01:00
|
|
|
logTicker *time.Ticker
|
2020-10-27 20:43:00 +01:00
|
|
|
workerChannel chan chan work
|
|
|
|
queue chan work // receives jobs to send to workers
|
|
|
|
end chan bool // when receives bool stops workers
|
2020-10-25 17:00:21 +01:00
|
|
|
//queue *dque.DQue
|
|
|
|
}
|
|
|
|
|
2020-10-28 01:12:27 +01:00
|
|
|
// TODO This hardcoded value will go away when the queue is persisted in disk
|
|
|
|
const bufferSize = 10000
|
|
|
|
|
2020-10-25 17:00:21 +01:00
|
|
|
func NewPool(name string, workerCount int, item interface{}, exec Executor) (*Pool, error) {
|
|
|
|
p := &Pool{
|
|
|
|
name: name,
|
|
|
|
item: item,
|
|
|
|
exec: exec,
|
2020-10-28 01:12:27 +01:00
|
|
|
queue: make(chan work, bufferSize),
|
2020-10-25 17:00:21 +01:00
|
|
|
end: make(chan bool),
|
|
|
|
}
|
|
|
|
|
|
|
|
//q, err := dque.NewOrOpen(name, filepath.Join(conf.Server.DataFolder, "queues", name), 50, p.itemBuilder)
|
|
|
|
//if err != nil {
|
|
|
|
// return nil, err
|
|
|
|
//}
|
|
|
|
//p.queue = q
|
2020-10-27 20:43:00 +01:00
|
|
|
|
|
|
|
p.workerChannel = make(chan chan work)
|
2020-10-25 17:00:21 +01:00
|
|
|
for i := 0; i < workerCount; i++ {
|
|
|
|
worker := worker{
|
|
|
|
p: p,
|
|
|
|
id: i,
|
|
|
|
channel: make(chan work),
|
2020-10-27 20:43:00 +01:00
|
|
|
workerChannel: p.workerChannel,
|
2020-10-25 17:00:21 +01:00
|
|
|
end: make(chan bool)}
|
|
|
|
worker.Start()
|
|
|
|
p.workers = append(p.workers, worker)
|
|
|
|
}
|
|
|
|
|
|
|
|
// start pool
|
|
|
|
go func() {
|
2020-10-30 21:08:43 +01:00
|
|
|
p.logTicker = time.NewTicker(10 * time.Second)
|
|
|
|
running := false
|
2020-10-25 17:00:21 +01:00
|
|
|
for {
|
|
|
|
select {
|
2020-10-30 21:08:43 +01:00
|
|
|
case <-p.logTicker.C:
|
|
|
|
if len(p.queue) > 0 {
|
|
|
|
log.Debug("Queue status", "pool", p.name, "items", len(p.queue))
|
|
|
|
} else {
|
|
|
|
if running {
|
2020-10-30 21:08:43 +01:00
|
|
|
log.Info("Queue empty", "pool", p.name)
|
2020-10-30 21:08:43 +01:00
|
|
|
}
|
|
|
|
running = false
|
|
|
|
}
|
2020-10-25 17:00:21 +01:00
|
|
|
case <-p.end:
|
|
|
|
for _, w := range p.workers {
|
|
|
|
w.Stop() // stop worker
|
|
|
|
}
|
|
|
|
return
|
|
|
|
case work := <-p.queue:
|
2020-10-30 21:08:43 +01:00
|
|
|
running = true
|
2020-10-27 20:43:00 +01:00
|
|
|
worker := <-p.workerChannel // wait for available channel
|
|
|
|
worker <- work // dispatch work to worker
|
2020-10-25 17:00:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
return p, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Pool) Submit(workload interface{}) {
|
|
|
|
p.queue <- work{workload}
|
|
|
|
}
|
|
|
|
|
|
|
|
//func (p *Pool) itemBuilder() interface{} {
|
|
|
|
// t := reflect.TypeOf(p.item)
|
|
|
|
// return reflect.New(t).Interface()
|
|
|
|
//}
|
|
|
|
//
|
|
|
|
type work struct {
|
|
|
|
workload interface{}
|
|
|
|
}
|
|
|
|
|
|
|
|
type worker struct {
|
|
|
|
id int
|
|
|
|
p *Pool
|
|
|
|
workerChannel chan chan work // used to communicate between dispatcher and workers
|
|
|
|
channel chan work
|
|
|
|
end chan bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// start worker
|
|
|
|
func (w *worker) Start() {
|
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
w.workerChannel <- w.channel // when the worker is available place channel in queue
|
|
|
|
select {
|
|
|
|
case job := <-w.channel: // worker has received job
|
|
|
|
w.p.exec(job.workload) // do work
|
|
|
|
case <-w.end:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
|
|
|
// end worker
|
|
|
|
func (w *worker) Stop() {
|
|
|
|
w.end <- true
|
|
|
|
}
|