// Copyright 2019 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. package queue import ( "context" "encoding/json" "fmt" "reflect" "sort" "sync" "time" "code.gitea.io/gitea/modules/log" ) var manager *Manager // Manager is a queue manager type Manager struct { mutex sync.Mutex counter int64 Queues map[int64]*ManagedQueue } // ManagedQueue represents a working queue inheriting from Gitea. type ManagedQueue struct { mutex sync.Mutex QID int64 Queue Queue Type Type Name string Configuration interface{} ExemplarType string Pool ManagedPool counter int64 PoolWorkers map[int64]*PoolWorkers } // ManagedPool is a simple interface to get certain details from a worker pool type ManagedPool interface { AddWorkers(number int, timeout time.Duration) context.CancelFunc NumberOfWorkers() int MaxNumberOfWorkers() int SetMaxNumberOfWorkers(int) BoostTimeout() time.Duration BlockTimeout() time.Duration BoostWorkers() int SetSettings(maxNumberOfWorkers, boostWorkers int, timeout time.Duration) } // ManagedQueueList implements the sort.Interface type ManagedQueueList []*ManagedQueue // PoolWorkers represents a working queue inheriting from Gitea. type PoolWorkers struct { PID int64 Workers int Start time.Time Timeout time.Time HasTimeout bool Cancel context.CancelFunc } // PoolWorkersList implements the sort.Interface type PoolWorkersList []*PoolWorkers func init() { _ = GetManager() } // GetManager returns a Manager and initializes one as singleton if there's none yet func GetManager() *Manager { if manager == nil { manager = &Manager{ Queues: make(map[int64]*ManagedQueue), } } return manager } // Add adds a queue to this manager func (m *Manager) Add(queue Queue, t Type, configuration, exemplar interface{}, pool ManagedPool) int64 { cfg, _ := json.Marshal(configuration) mq := &ManagedQueue{ Queue: queue, Type: t, Configuration: string(cfg), ExemplarType: reflect.TypeOf(exemplar).String(), PoolWorkers: make(map[int64]*PoolWorkers), Pool: pool, } m.mutex.Lock() m.counter++ mq.QID = m.counter mq.Name = fmt.Sprintf("queue-%d", mq.QID) if named, ok := queue.(Named); ok { mq.Name = named.Name() } m.Queues[mq.QID] = mq m.mutex.Unlock() log.Trace("Queue Manager registered: %s (QID: %d)", mq.Name, mq.QID) return mq.QID } // Remove a queue from the Manager func (m *Manager) Remove(qid int64) { m.mutex.Lock() delete(m.Queues, qid) m.mutex.Unlock() log.Trace("Queue Manager removed: QID: %d", qid) } // GetManagedQueue by qid func (m *Manager) GetManagedQueue(qid int64) *ManagedQueue { m.mutex.Lock() defer m.mutex.Unlock() return m.Queues[qid] } // ManagedQueues returns the managed queues func (m *Manager) ManagedQueues() []*ManagedQueue { m.mutex.Lock() mqs := make([]*ManagedQueue, 0, len(m.Queues)) for _, mq := range m.Queues { mqs = append(mqs, mq) } m.mutex.Unlock() sort.Sort(ManagedQueueList(mqs)) return mqs } // Workers returns the poolworkers func (q *ManagedQueue) Workers() []*PoolWorkers { q.mutex.Lock() workers := make([]*PoolWorkers, 0, len(q.PoolWorkers)) for _, worker := range q.PoolWorkers { workers = append(workers, worker) } q.mutex.Unlock() sort.Sort(PoolWorkersList(workers)) return workers } // RegisterWorkers registers workers to this queue func (q *ManagedQueue) RegisterWorkers(number int, start time.Time, hasTimeout bool, timeout time.Time, cancel context.CancelFunc) int64 { q.mutex.Lock() defer q.mutex.Unlock() q.counter++ q.PoolWorkers[q.counter] = &PoolWorkers{ PID: q.counter, Workers: number, Start: start, Timeout: timeout, HasTimeout: hasTimeout, Cancel: cancel, } return q.counter } // CancelWorkers cancels pooled workers with pid func (q *ManagedQueue) CancelWorkers(pid int64) { q.mutex.Lock() pw, ok := q.PoolWorkers[pid] q.mutex.Unlock() if !ok { return } pw.Cancel() } // RemoveWorkers deletes pooled workers with pid func (q *ManagedQueue) RemoveWorkers(pid int64) { q.mutex.Lock() pw, ok := q.PoolWorkers[pid] delete(q.PoolWorkers, pid) q.mutex.Unlock() if ok && pw.Cancel != nil { pw.Cancel() } } // AddWorkers adds workers to the queue if it has registered an add worker function func (q *ManagedQueue) AddWorkers(number int, timeout time.Duration) context.CancelFunc { if q.Pool != nil { // the cancel will be added to the pool workers description above return q.Pool.AddWorkers(number, timeout) } return nil } // NumberOfWorkers returns the number of workers in the queue func (q *ManagedQueue) NumberOfWorkers() int { if q.Pool != nil { return q.Pool.NumberOfWorkers() } return -1 } // MaxNumberOfWorkers returns the maximum number of workers for the pool func (q *ManagedQueue) MaxNumberOfWorkers() int { if q.Pool != nil { return q.Pool.MaxNumberOfWorkers() } return 0 } // BoostWorkers returns the number of workers for a boost func (q *ManagedQueue) BoostWorkers() int { if q.Pool != nil { return q.Pool.BoostWorkers() } return -1 } // BoostTimeout returns the timeout of the next boost func (q *ManagedQueue) BoostTimeout() time.Duration { if q.Pool != nil { return q.Pool.BoostTimeout() } return 0 } // BlockTimeout returns the timeout til the next boost func (q *ManagedQueue) BlockTimeout() time.Duration { if q.Pool != nil { return q.Pool.BlockTimeout() } return 0 } // SetSettings sets the setable boost values func (q *ManagedQueue) SetSettings(maxNumberOfWorkers, boostWorkers int, timeout time.Duration) { if q.Pool != nil { q.Pool.SetSettings(maxNumberOfWorkers, boostWorkers, timeout) } } func (l ManagedQueueList) Len() int { return len(l) } func (l ManagedQueueList) Less(i, j int) bool { return l[i].Name < l[j].Name } func (l ManagedQueueList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } func (l PoolWorkersList) Len() int { return len(l) } func (l PoolWorkersList) Less(i, j int) bool { return l[i].Start.Before(l[j].Start) } func (l PoolWorkersList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }