package queue import ( "fmt" "strings" ) type QueuePath []string //nolint:revive // TreeQueue is an hierarchical queue implementation where each sub-queue // has the same guarantees to be chosen from. // Each queue has also a local queue, which gets chosen with equal preference as the sub-queues. type TreeQueue struct { // local queue ch RequestChannel // index of where this item is located in the mapping pos QueueIndex // index of the sub-queues current QueueIndex // mapping for sub-queues mapping *Mapping[*TreeQueue] // name of the queue name string // maximum queue size of the local queue size int } // newTreeQueue creates a new TreeQueue instance func newTreeQueue(size int, name string) *TreeQueue { m := &Mapping[*TreeQueue]{} m.Init(64) // TODO(chaudum): What is a good initial value? return &TreeQueue{ ch: make(RequestChannel, size), pos: StartIndexWithLocalQueue, current: StartIndexWithLocalQueue, mapping: m, name: name, size: size, } } // add recursively adds queues based on given path func (q *TreeQueue) add(path QueuePath) *TreeQueue { if len(path) == 0 { return q } curr, remaining := path[0], path[1:] queue, created := q.getOrCreate(curr) if created { q.mapping.Put(queue.Name(), queue) } return queue.add(remaining) } func (q *TreeQueue) getOrCreate(name string) (subq *TreeQueue, created bool) { subq = q.mapping.GetByKey(name) if subq == nil { subq = newTreeQueue(q.size, name) created = true } return subq, created } // Chan implements Queue func (q *TreeQueue) Chan() RequestChannel { return q.ch } // Dequeue implements Queue func (q *TreeQueue) Dequeue() Request { var item Request // shortcut of there are not sub-queues // always use local queue if q.mapping.Len() == 0 { if len(q.ch) > 0 { return <-q.ch } return nil } maxIter := len(q.mapping.keys) + 1 for iters := 0; iters < maxIter; iters++ { if q.current == StartIndexWithLocalQueue { q.current++ if len(q.ch) > 0 { item = <-q.ch if item != nil { return item } } } subq, err := q.mapping.GetNext(q.current) if err == ErrOutOfBounds { q.current = StartIndexWithLocalQueue continue } if subq != nil { q.current = subq.pos item := subq.Dequeue() if item != nil { if subq.Len() == 0 { q.mapping.Remove(subq.name) } return item } } } return nil } // Name implements Queue func (q *TreeQueue) Name() string { return q.name } // Len implements Queue // It returns the length of the local queue and all sub-queues. // This may be expensive depending on the size of the queue tree. func (q *TreeQueue) Len() int { count := len(q.ch) for _, subq := range q.mapping.Values() { count += subq.Len() } return count } // Index implements Mapable func (q *TreeQueue) Pos() QueueIndex { return q.pos } // Index implements Mapable func (q *TreeQueue) SetPos(index QueueIndex) { q.pos = index } // String makes the queue printable func (q *TreeQueue) String() string { sb := &strings.Builder{} sb.WriteString("{") fmt.Fprintf(sb, "name=%s, len=%d/%d, children=[", q.Name(), q.Len(), cap(q.ch)) subqs := q.mapping.Values() for i, m := range subqs { sb.WriteString(m.String()) if i < len(subqs)-1 { sb.WriteString(",") } } sb.WriteString("]") sb.WriteString("}") return sb.String() }