Semaphore/api/tasks/pool.go

128 lines
2.3 KiB
Go
Raw Normal View History

2016-04-04 15:44:34 +02:00
package tasks
import (
"strconv"
"time"
log "github.com/Sirupsen/logrus"
"github.com/ansible-semaphore/semaphore/util"
2016-04-04 15:44:34 +02:00
)
type taskPool struct {
queue []*task
register chan *task
activeProj map[int]*task
activeNodes map[string]*task
2017-08-19 10:45:01 +02:00
running int
2016-04-04 15:44:34 +02:00
}
var pool = taskPool{
queue: make([]*task, 0),
register: make(chan *task),
activeProj: make(map[int]*task),
activeNodes: make(map[string]*task),
2017-08-19 10:45:01 +02:00
running: 0,
}
type resourceLock struct {
2017-08-19 10:45:01 +02:00
lock bool
holder *task
2016-04-04 15:44:34 +02:00
}
var resourceLocker = make(chan *resourceLock)
//nolint: gocyclo
2016-04-04 15:44:34 +02:00
func (p *taskPool) run() {
ticker := time.NewTicker(5 * time.Second)
defer func() {
2017-08-19 10:45:01 +02:00
close(resourceLocker)
ticker.Stop()
}()
// Lock or unlock resources when running a task
2017-08-19 10:45:01 +02:00
go func(locker <-chan *resourceLock) {
for l := range locker {
t := l.holder
2017-08-19 10:45:01 +02:00
if l.lock {
if p.blocks(t) {
panic("Trying to lock an already locked resource!")
}
2017-08-19 10:45:01 +02:00
p.activeProj[t.projectID] = t
2017-08-19 10:45:01 +02:00
for _, node := range t.hosts {
p.activeNodes[node] = t
}
2017-08-19 10:45:01 +02:00
p.running++
continue
}
if p.activeProj[t.projectID] == t {
delete(p.activeProj, t.projectID)
}
2017-08-19 10:45:01 +02:00
for _, node := range t.hosts {
delete(p.activeNodes, node)
}
p.running--
}
}(resourceLocker)
2016-04-04 15:44:34 +02:00
for {
select {
case task := <-p.register:
p.queue = append(p.queue, task)
log.Debug(task)
task.log("Task " + strconv.Itoa(task.task.ID) + " added to queue")
case <-ticker.C:
if len(p.queue) == 0 {
continue
2018-06-07 09:29:55 +02:00
} else if t := p.queue[0]; t.task.Status != taskFailStatus && p.blocks(t) {
p.queue = append(p.queue[1:], t)
continue
}
2018-06-07 09:29:55 +02:00
if t := p.queue[0]; t.task.Status != taskFailStatus {
log.Info("Set resourse locker with task " + strconv.Itoa(t.task.ID))
resourceLocker <- &resourceLock{lock: true, holder: t}
if !t.prepared {
2018-06-07 09:29:55 +02:00
go t.prepareRun()
continue
2018-06-07 09:29:55 +02:00
}
go t.run()
p.queue = p.queue[1:]
}
2016-04-04 15:44:34 +02:00
}
}
}
func (p *taskPool) blocks(t *task) bool {
if p.running >= util.Config.MaxParallelTasks {
return true
}
2017-08-19 10:45:01 +02:00
switch util.Config.ConcurrencyMode {
case "project":
return p.activeProj[t.projectID] != nil
case "node":
for _, node := range t.hosts {
if p.activeNodes[node] != nil {
2017-08-19 10:45:01 +02:00
return true
}
}
2017-08-19 10:45:01 +02:00
return false
default:
return p.running > 0
}
}
// StartRunner begins the task pool, used as a goroutine
2016-04-04 15:44:34 +02:00
func StartRunner() {
pool.run()
}