|
|
@@ -1,345 +0,0 @@
|
|
|
-// Copyright 2019 TuxSH
|
|
|
-// Licensed under GPLv2 or any later version
|
|
|
-// Refer to the license.txt file included.
|
|
|
-
|
|
|
-#pragma once
|
|
|
-
|
|
|
-#include <array>
|
|
|
-#include <iterator>
|
|
|
-#include <list>
|
|
|
-#include <utility>
|
|
|
-
|
|
|
-#include "common/bit_util.h"
|
|
|
-#include "common/common_types.h"
|
|
|
-
|
|
|
-namespace Common {
|
|
|
-
|
|
|
-/**
|
|
|
- * A MultiLevelQueue is a type of priority queue which has the following characteristics:
|
|
|
- * - iteratable through each of its elements.
|
|
|
- * - back can be obtained.
|
|
|
- * - O(1) add, lookup (both front and back)
|
|
|
- * - discrete priorities and a max of 64 priorities (limited domain)
|
|
|
- * This type of priority queue is normaly used for managing threads within an scheduler
|
|
|
- */
|
|
|
-template <typename T, std::size_t Depth>
|
|
|
-class MultiLevelQueue {
|
|
|
-public:
|
|
|
- using value_type = T;
|
|
|
- using reference = value_type&;
|
|
|
- using const_reference = const value_type&;
|
|
|
- using pointer = value_type*;
|
|
|
- using const_pointer = const value_type*;
|
|
|
-
|
|
|
- using difference_type = typename std::pointer_traits<pointer>::difference_type;
|
|
|
- using size_type = std::size_t;
|
|
|
-
|
|
|
- template <bool is_constant>
|
|
|
- class iterator_impl {
|
|
|
- public:
|
|
|
- using iterator_category = std::bidirectional_iterator_tag;
|
|
|
- using value_type = T;
|
|
|
- using pointer = std::conditional_t<is_constant, T*, const T*>;
|
|
|
- using reference = std::conditional_t<is_constant, const T&, T&>;
|
|
|
- using difference_type = typename std::pointer_traits<pointer>::difference_type;
|
|
|
-
|
|
|
- friend bool operator==(const iterator_impl& lhs, const iterator_impl& rhs) {
|
|
|
- if (lhs.IsEnd() && rhs.IsEnd())
|
|
|
- return true;
|
|
|
- return std::tie(lhs.current_priority, lhs.it) == std::tie(rhs.current_priority, rhs.it);
|
|
|
- }
|
|
|
-
|
|
|
- friend bool operator!=(const iterator_impl& lhs, const iterator_impl& rhs) {
|
|
|
- return !operator==(lhs, rhs);
|
|
|
- }
|
|
|
-
|
|
|
- reference operator*() const {
|
|
|
- return *it;
|
|
|
- }
|
|
|
-
|
|
|
- pointer operator->() const {
|
|
|
- return it.operator->();
|
|
|
- }
|
|
|
-
|
|
|
- iterator_impl& operator++() {
|
|
|
- if (IsEnd()) {
|
|
|
- return *this;
|
|
|
- }
|
|
|
-
|
|
|
- ++it;
|
|
|
-
|
|
|
- if (it == GetEndItForPrio()) {
|
|
|
- u64 prios = mlq.used_priorities;
|
|
|
- prios &= ~((1ULL << (current_priority + 1)) - 1);
|
|
|
- if (prios == 0) {
|
|
|
- current_priority = static_cast<u32>(mlq.depth());
|
|
|
- } else {
|
|
|
- current_priority = CountTrailingZeroes64(prios);
|
|
|
- it = GetBeginItForPrio();
|
|
|
- }
|
|
|
- }
|
|
|
- return *this;
|
|
|
- }
|
|
|
-
|
|
|
- iterator_impl& operator--() {
|
|
|
- if (IsEnd()) {
|
|
|
- if (mlq.used_priorities != 0) {
|
|
|
- current_priority = 63 - CountLeadingZeroes64(mlq.used_priorities);
|
|
|
- it = GetEndItForPrio();
|
|
|
- --it;
|
|
|
- }
|
|
|
- } else if (it == GetBeginItForPrio()) {
|
|
|
- u64 prios = mlq.used_priorities;
|
|
|
- prios &= (1ULL << current_priority) - 1;
|
|
|
- if (prios != 0) {
|
|
|
- current_priority = CountTrailingZeroes64(prios);
|
|
|
- it = GetEndItForPrio();
|
|
|
- --it;
|
|
|
- }
|
|
|
- } else {
|
|
|
- --it;
|
|
|
- }
|
|
|
- return *this;
|
|
|
- }
|
|
|
-
|
|
|
- iterator_impl operator++(int) {
|
|
|
- const iterator_impl v{*this};
|
|
|
- ++(*this);
|
|
|
- return v;
|
|
|
- }
|
|
|
-
|
|
|
- iterator_impl operator--(int) {
|
|
|
- const iterator_impl v{*this};
|
|
|
- --(*this);
|
|
|
- return v;
|
|
|
- }
|
|
|
-
|
|
|
- // allow implicit const->non-const
|
|
|
- iterator_impl(const iterator_impl<false>& other)
|
|
|
- : mlq(other.mlq), it(other.it), current_priority(other.current_priority) {}
|
|
|
-
|
|
|
- iterator_impl(const iterator_impl<true>& other)
|
|
|
- : mlq(other.mlq), it(other.it), current_priority(other.current_priority) {}
|
|
|
-
|
|
|
- iterator_impl& operator=(const iterator_impl<false>& other) {
|
|
|
- mlq = other.mlq;
|
|
|
- it = other.it;
|
|
|
- current_priority = other.current_priority;
|
|
|
- return *this;
|
|
|
- }
|
|
|
-
|
|
|
- friend class iterator_impl<true>;
|
|
|
- iterator_impl() = default;
|
|
|
-
|
|
|
- private:
|
|
|
- friend class MultiLevelQueue;
|
|
|
- using container_ref =
|
|
|
- std::conditional_t<is_constant, const MultiLevelQueue&, MultiLevelQueue&>;
|
|
|
- using list_iterator = std::conditional_t<is_constant, typename std::list<T>::const_iterator,
|
|
|
- typename std::list<T>::iterator>;
|
|
|
-
|
|
|
- explicit iterator_impl(container_ref mlq, list_iterator it, u32 current_priority)
|
|
|
- : mlq(mlq), it(it), current_priority(current_priority) {}
|
|
|
- explicit iterator_impl(container_ref mlq, u32 current_priority)
|
|
|
- : mlq(mlq), it(), current_priority(current_priority) {}
|
|
|
-
|
|
|
- bool IsEnd() const {
|
|
|
- return current_priority == mlq.depth();
|
|
|
- }
|
|
|
-
|
|
|
- list_iterator GetBeginItForPrio() const {
|
|
|
- return mlq.levels[current_priority].begin();
|
|
|
- }
|
|
|
-
|
|
|
- list_iterator GetEndItForPrio() const {
|
|
|
- return mlq.levels[current_priority].end();
|
|
|
- }
|
|
|
-
|
|
|
- container_ref mlq;
|
|
|
- list_iterator it;
|
|
|
- u32 current_priority;
|
|
|
- };
|
|
|
-
|
|
|
- using iterator = iterator_impl<false>;
|
|
|
- using const_iterator = iterator_impl<true>;
|
|
|
-
|
|
|
- void add(const T& element, u32 priority, bool send_back = true) {
|
|
|
- if (send_back)
|
|
|
- levels[priority].push_back(element);
|
|
|
- else
|
|
|
- levels[priority].push_front(element);
|
|
|
- used_priorities |= 1ULL << priority;
|
|
|
- }
|
|
|
-
|
|
|
- void remove(const T& element, u32 priority) {
|
|
|
- auto it = ListIterateTo(levels[priority], element);
|
|
|
- if (it == levels[priority].end())
|
|
|
- return;
|
|
|
- levels[priority].erase(it);
|
|
|
- if (levels[priority].empty()) {
|
|
|
- used_priorities &= ~(1ULL << priority);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- void adjust(const T& element, u32 old_priority, u32 new_priority, bool adjust_front = false) {
|
|
|
- remove(element, old_priority);
|
|
|
- add(element, new_priority, !adjust_front);
|
|
|
- }
|
|
|
- void adjust(const_iterator it, u32 old_priority, u32 new_priority, bool adjust_front = false) {
|
|
|
- adjust(*it, old_priority, new_priority, adjust_front);
|
|
|
- }
|
|
|
-
|
|
|
- void transfer_to_front(const T& element, u32 priority, MultiLevelQueue& other) {
|
|
|
- ListSplice(other.levels[priority], other.levels[priority].begin(), levels[priority],
|
|
|
- ListIterateTo(levels[priority], element));
|
|
|
-
|
|
|
- other.used_priorities |= 1ULL << priority;
|
|
|
-
|
|
|
- if (levels[priority].empty()) {
|
|
|
- used_priorities &= ~(1ULL << priority);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- void transfer_to_front(const_iterator it, u32 priority, MultiLevelQueue& other) {
|
|
|
- transfer_to_front(*it, priority, other);
|
|
|
- }
|
|
|
-
|
|
|
- void transfer_to_back(const T& element, u32 priority, MultiLevelQueue& other) {
|
|
|
- ListSplice(other.levels[priority], other.levels[priority].end(), levels[priority],
|
|
|
- ListIterateTo(levels[priority], element));
|
|
|
-
|
|
|
- other.used_priorities |= 1ULL << priority;
|
|
|
-
|
|
|
- if (levels[priority].empty()) {
|
|
|
- used_priorities &= ~(1ULL << priority);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- void transfer_to_back(const_iterator it, u32 priority, MultiLevelQueue& other) {
|
|
|
- transfer_to_back(*it, priority, other);
|
|
|
- }
|
|
|
-
|
|
|
- void yield(u32 priority, std::size_t n = 1) {
|
|
|
- ListShiftForward(levels[priority], n);
|
|
|
- }
|
|
|
-
|
|
|
- [[nodiscard]] std::size_t depth() const {
|
|
|
- return Depth;
|
|
|
- }
|
|
|
-
|
|
|
- [[nodiscard]] std::size_t size(u32 priority) const {
|
|
|
- return levels[priority].size();
|
|
|
- }
|
|
|
-
|
|
|
- [[nodiscard]] std::size_t size() const {
|
|
|
- u64 priorities = used_priorities;
|
|
|
- std::size_t size = 0;
|
|
|
- while (priorities != 0) {
|
|
|
- const u64 current_priority = CountTrailingZeroes64(priorities);
|
|
|
- size += levels[current_priority].size();
|
|
|
- priorities &= ~(1ULL << current_priority);
|
|
|
- }
|
|
|
- return size;
|
|
|
- }
|
|
|
-
|
|
|
- [[nodiscard]] bool empty() const {
|
|
|
- return used_priorities == 0;
|
|
|
- }
|
|
|
-
|
|
|
- [[nodiscard]] bool empty(u32 priority) const {
|
|
|
- return (used_priorities & (1ULL << priority)) == 0;
|
|
|
- }
|
|
|
-
|
|
|
- [[nodiscard]] u32 highest_priority_set(u32 max_priority = 0) const {
|
|
|
- const u64 priorities =
|
|
|
- max_priority == 0 ? used_priorities : (used_priorities & ~((1ULL << max_priority) - 1));
|
|
|
- return priorities == 0 ? Depth : static_cast<u32>(CountTrailingZeroes64(priorities));
|
|
|
- }
|
|
|
-
|
|
|
- [[nodiscard]] u32 lowest_priority_set(u32 min_priority = Depth - 1) const {
|
|
|
- const u64 priorities = min_priority >= Depth - 1
|
|
|
- ? used_priorities
|
|
|
- : (used_priorities & ((1ULL << (min_priority + 1)) - 1));
|
|
|
- return priorities == 0 ? Depth : 63 - CountLeadingZeroes64(priorities);
|
|
|
- }
|
|
|
-
|
|
|
- [[nodiscard]] const_iterator cbegin(u32 max_prio = 0) const {
|
|
|
- const u32 priority = highest_priority_set(max_prio);
|
|
|
- return priority == Depth ? cend()
|
|
|
- : const_iterator{*this, levels[priority].cbegin(), priority};
|
|
|
- }
|
|
|
- [[nodiscard]] const_iterator begin(u32 max_prio = 0) const {
|
|
|
- return cbegin(max_prio);
|
|
|
- }
|
|
|
- [[nodiscard]] iterator begin(u32 max_prio = 0) {
|
|
|
- const u32 priority = highest_priority_set(max_prio);
|
|
|
- return priority == Depth ? end() : iterator{*this, levels[priority].begin(), priority};
|
|
|
- }
|
|
|
-
|
|
|
- [[nodiscard]] const_iterator cend(u32 min_prio = Depth - 1) const {
|
|
|
- return min_prio == Depth - 1 ? const_iterator{*this, Depth} : cbegin(min_prio + 1);
|
|
|
- }
|
|
|
- [[nodiscard]] const_iterator end(u32 min_prio = Depth - 1) const {
|
|
|
- return cend(min_prio);
|
|
|
- }
|
|
|
- [[nodiscard]] iterator end(u32 min_prio = Depth - 1) {
|
|
|
- return min_prio == Depth - 1 ? iterator{*this, Depth} : begin(min_prio + 1);
|
|
|
- }
|
|
|
-
|
|
|
- [[nodiscard]] T& front(u32 max_priority = 0) {
|
|
|
- const u32 priority = highest_priority_set(max_priority);
|
|
|
- return levels[priority == Depth ? 0 : priority].front();
|
|
|
- }
|
|
|
- [[nodiscard]] const T& front(u32 max_priority = 0) const {
|
|
|
- const u32 priority = highest_priority_set(max_priority);
|
|
|
- return levels[priority == Depth ? 0 : priority].front();
|
|
|
- }
|
|
|
-
|
|
|
- [[nodiscard]] T& back(u32 min_priority = Depth - 1) {
|
|
|
- const u32 priority = lowest_priority_set(min_priority); // intended
|
|
|
- return levels[priority == Depth ? 63 : priority].back();
|
|
|
- }
|
|
|
- [[nodiscard]] const T& back(u32 min_priority = Depth - 1) const {
|
|
|
- const u32 priority = lowest_priority_set(min_priority); // intended
|
|
|
- return levels[priority == Depth ? 63 : priority].back();
|
|
|
- }
|
|
|
-
|
|
|
- void clear() {
|
|
|
- used_priorities = 0;
|
|
|
- for (std::size_t i = 0; i < Depth; i++) {
|
|
|
- levels[i].clear();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-private:
|
|
|
- using const_list_iterator = typename std::list<T>::const_iterator;
|
|
|
-
|
|
|
- static void ListShiftForward(std::list<T>& list, const std::size_t shift = 1) {
|
|
|
- if (shift >= list.size()) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- const auto begin_range = list.begin();
|
|
|
- const auto end_range = std::next(begin_range, shift);
|
|
|
- list.splice(list.end(), list, begin_range, end_range);
|
|
|
- }
|
|
|
-
|
|
|
- static void ListSplice(std::list<T>& in_list, const_list_iterator position,
|
|
|
- std::list<T>& out_list, const_list_iterator element) {
|
|
|
- in_list.splice(position, out_list, element);
|
|
|
- }
|
|
|
-
|
|
|
- [[nodiscard]] static const_list_iterator ListIterateTo(const std::list<T>& list,
|
|
|
- const T& element) {
|
|
|
- auto it = list.cbegin();
|
|
|
- while (it != list.cend() && *it != element) {
|
|
|
- ++it;
|
|
|
- }
|
|
|
- return it;
|
|
|
- }
|
|
|
-
|
|
|
- std::array<std::list<T>, Depth> levels;
|
|
|
- u64 used_priorities = 0;
|
|
|
-};
|
|
|
-
|
|
|
-} // namespace Common
|