multi_level_queue.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. // Copyright 2019 TuxSH
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #pragma once
  5. #include <array>
  6. #include <iterator>
  7. #include <list>
  8. #include <utility>
  9. #include "common/bit_util.h"
  10. #include "common/common_types.h"
  11. namespace Common {
  12. /**
  13. * A MultiLevelQueue is a type of priority queue which has the following characteristics:
  14. * - iteratable through each of its elements.
  15. * - back can be obtained.
  16. * - O(1) add, lookup (both front and back)
  17. * - discrete priorities and a max of 64 priorities (limited domain)
  18. * This type of priority queue is normaly used for managing threads within an scheduler
  19. */
  20. template <typename T, std::size_t Depth>
  21. class MultiLevelQueue {
  22. public:
  23. using value_type = T;
  24. using reference = value_type&;
  25. using const_reference = const value_type&;
  26. using pointer = value_type*;
  27. using const_pointer = const value_type*;
  28. using difference_type = typename std::pointer_traits<pointer>::difference_type;
  29. using size_type = std::size_t;
  30. template <bool is_constant>
  31. class iterator_impl {
  32. public:
  33. using iterator_category = std::bidirectional_iterator_tag;
  34. using value_type = T;
  35. using pointer = std::conditional_t<is_constant, T*, const T*>;
  36. using reference = std::conditional_t<is_constant, const T&, T&>;
  37. using difference_type = typename std::pointer_traits<pointer>::difference_type;
  38. friend bool operator==(const iterator_impl& lhs, const iterator_impl& rhs) {
  39. if (lhs.IsEnd() && rhs.IsEnd())
  40. return true;
  41. return std::tie(lhs.current_priority, lhs.it) == std::tie(rhs.current_priority, rhs.it);
  42. }
  43. friend bool operator!=(const iterator_impl& lhs, const iterator_impl& rhs) {
  44. return !operator==(lhs, rhs);
  45. }
  46. reference operator*() const {
  47. return *it;
  48. }
  49. pointer operator->() const {
  50. return it.operator->();
  51. }
  52. iterator_impl& operator++() {
  53. if (IsEnd()) {
  54. return *this;
  55. }
  56. ++it;
  57. if (it == GetEndItForPrio()) {
  58. u64 prios = mlq.used_priorities;
  59. prios &= ~((1ULL << (current_priority + 1)) - 1);
  60. if (prios == 0) {
  61. current_priority = static_cast<u32>(mlq.depth());
  62. } else {
  63. current_priority = CountTrailingZeroes64(prios);
  64. it = GetBeginItForPrio();
  65. }
  66. }
  67. return *this;
  68. }
  69. iterator_impl& operator--() {
  70. if (IsEnd()) {
  71. if (mlq.used_priorities != 0) {
  72. current_priority = 63 - CountLeadingZeroes64(mlq.used_priorities);
  73. it = GetEndItForPrio();
  74. --it;
  75. }
  76. } else if (it == GetBeginItForPrio()) {
  77. u64 prios = mlq.used_priorities;
  78. prios &= (1ULL << current_priority) - 1;
  79. if (prios != 0) {
  80. current_priority = CountTrailingZeroes64(prios);
  81. it = GetEndItForPrio();
  82. --it;
  83. }
  84. } else {
  85. --it;
  86. }
  87. return *this;
  88. }
  89. iterator_impl operator++(int) {
  90. const iterator_impl v{*this};
  91. ++(*this);
  92. return v;
  93. }
  94. iterator_impl operator--(int) {
  95. const iterator_impl v{*this};
  96. --(*this);
  97. return v;
  98. }
  99. // allow implicit const->non-const
  100. iterator_impl(const iterator_impl<false>& other)
  101. : mlq(other.mlq), it(other.it), current_priority(other.current_priority) {}
  102. iterator_impl(const iterator_impl<true>& other)
  103. : mlq(other.mlq), it(other.it), current_priority(other.current_priority) {}
  104. iterator_impl& operator=(const iterator_impl<false>& other) {
  105. mlq = other.mlq;
  106. it = other.it;
  107. current_priority = other.current_priority;
  108. return *this;
  109. }
  110. friend class iterator_impl<true>;
  111. iterator_impl() = default;
  112. private:
  113. friend class MultiLevelQueue;
  114. using container_ref =
  115. std::conditional_t<is_constant, const MultiLevelQueue&, MultiLevelQueue&>;
  116. using list_iterator = std::conditional_t<is_constant, typename std::list<T>::const_iterator,
  117. typename std::list<T>::iterator>;
  118. explicit iterator_impl(container_ref mlq, list_iterator it, u32 current_priority)
  119. : mlq(mlq), it(it), current_priority(current_priority) {}
  120. explicit iterator_impl(container_ref mlq, u32 current_priority)
  121. : mlq(mlq), it(), current_priority(current_priority) {}
  122. bool IsEnd() const {
  123. return current_priority == mlq.depth();
  124. }
  125. list_iterator GetBeginItForPrio() const {
  126. return mlq.levels[current_priority].begin();
  127. }
  128. list_iterator GetEndItForPrio() const {
  129. return mlq.levels[current_priority].end();
  130. }
  131. container_ref mlq;
  132. list_iterator it;
  133. u32 current_priority;
  134. };
  135. using iterator = iterator_impl<false>;
  136. using const_iterator = iterator_impl<true>;
  137. void add(const T& element, u32 priority, bool send_back = true) {
  138. if (send_back)
  139. levels[priority].push_back(element);
  140. else
  141. levels[priority].push_front(element);
  142. used_priorities |= 1ULL << priority;
  143. }
  144. void remove(const T& element, u32 priority) {
  145. auto it = ListIterateTo(levels[priority], element);
  146. if (it == levels[priority].end())
  147. return;
  148. levels[priority].erase(it);
  149. if (levels[priority].empty()) {
  150. used_priorities &= ~(1ULL << priority);
  151. }
  152. }
  153. void adjust(const T& element, u32 old_priority, u32 new_priority, bool adjust_front = false) {
  154. remove(element, old_priority);
  155. add(element, new_priority, !adjust_front);
  156. }
  157. void adjust(const_iterator it, u32 old_priority, u32 new_priority, bool adjust_front = false) {
  158. adjust(*it, old_priority, new_priority, adjust_front);
  159. }
  160. void transfer_to_front(const T& element, u32 priority, MultiLevelQueue& other) {
  161. ListSplice(other.levels[priority], other.levels[priority].begin(), levels[priority],
  162. ListIterateTo(levels[priority], element));
  163. other.used_priorities |= 1ULL << priority;
  164. if (levels[priority].empty()) {
  165. used_priorities &= ~(1ULL << priority);
  166. }
  167. }
  168. void transfer_to_front(const_iterator it, u32 priority, MultiLevelQueue& other) {
  169. transfer_to_front(*it, priority, other);
  170. }
  171. void transfer_to_back(const T& element, u32 priority, MultiLevelQueue& other) {
  172. ListSplice(other.levels[priority], other.levels[priority].end(), levels[priority],
  173. ListIterateTo(levels[priority], element));
  174. other.used_priorities |= 1ULL << priority;
  175. if (levels[priority].empty()) {
  176. used_priorities &= ~(1ULL << priority);
  177. }
  178. }
  179. void transfer_to_back(const_iterator it, u32 priority, MultiLevelQueue& other) {
  180. transfer_to_back(*it, priority, other);
  181. }
  182. void yield(u32 priority, std::size_t n = 1) {
  183. ListShiftForward(levels[priority], n);
  184. }
  185. std::size_t depth() const {
  186. return Depth;
  187. }
  188. std::size_t size(u32 priority) const {
  189. return levels[priority].size();
  190. }
  191. std::size_t size() const {
  192. u64 priorities = used_priorities;
  193. std::size_t size = 0;
  194. while (priorities != 0) {
  195. const u64 current_priority = CountTrailingZeroes64(priorities);
  196. size += levels[current_priority].size();
  197. priorities &= ~(1ULL << current_priority);
  198. }
  199. return size;
  200. }
  201. bool empty() const {
  202. return used_priorities == 0;
  203. }
  204. bool empty(u32 priority) const {
  205. return (used_priorities & (1ULL << priority)) == 0;
  206. }
  207. u32 highest_priority_set(u32 max_priority = 0) const {
  208. const u64 priorities =
  209. max_priority == 0 ? used_priorities : (used_priorities & ~((1ULL << max_priority) - 1));
  210. return priorities == 0 ? Depth : static_cast<u32>(CountTrailingZeroes64(priorities));
  211. }
  212. u32 lowest_priority_set(u32 min_priority = Depth - 1) const {
  213. const u64 priorities = min_priority >= Depth - 1
  214. ? used_priorities
  215. : (used_priorities & ((1ULL << (min_priority + 1)) - 1));
  216. return priorities == 0 ? Depth : 63 - CountLeadingZeroes64(priorities);
  217. }
  218. const_iterator cbegin(u32 max_prio = 0) const {
  219. const u32 priority = highest_priority_set(max_prio);
  220. return priority == Depth ? cend()
  221. : const_iterator{*this, levels[priority].cbegin(), priority};
  222. }
  223. const_iterator begin(u32 max_prio = 0) const {
  224. return cbegin(max_prio);
  225. }
  226. iterator begin(u32 max_prio = 0) {
  227. const u32 priority = highest_priority_set(max_prio);
  228. return priority == Depth ? end() : iterator{*this, levels[priority].begin(), priority};
  229. }
  230. const_iterator cend(u32 min_prio = Depth - 1) const {
  231. return min_prio == Depth - 1 ? const_iterator{*this, Depth} : cbegin(min_prio + 1);
  232. }
  233. const_iterator end(u32 min_prio = Depth - 1) const {
  234. return cend(min_prio);
  235. }
  236. iterator end(u32 min_prio = Depth - 1) {
  237. return min_prio == Depth - 1 ? iterator{*this, Depth} : begin(min_prio + 1);
  238. }
  239. T& front(u32 max_priority = 0) {
  240. const u32 priority = highest_priority_set(max_priority);
  241. return levels[priority == Depth ? 0 : priority].front();
  242. }
  243. const T& front(u32 max_priority = 0) const {
  244. const u32 priority = highest_priority_set(max_priority);
  245. return levels[priority == Depth ? 0 : priority].front();
  246. }
  247. T back(u32 min_priority = Depth - 1) {
  248. const u32 priority = lowest_priority_set(min_priority); // intended
  249. return levels[priority == Depth ? 63 : priority].back();
  250. }
  251. const T& back(u32 min_priority = Depth - 1) const {
  252. const u32 priority = lowest_priority_set(min_priority); // intended
  253. return levels[priority == Depth ? 63 : priority].back();
  254. }
  255. private:
  256. using const_list_iterator = typename std::list<T>::const_iterator;
  257. static void ListShiftForward(std::list<T>& list, const std::size_t shift = 1) {
  258. if (shift >= list.size()) {
  259. return;
  260. }
  261. const auto begin_range = list.begin();
  262. const auto end_range = std::next(begin_range, shift);
  263. list.splice(list.end(), list, begin_range, end_range);
  264. }
  265. static void ListSplice(std::list<T>& in_list, const_list_iterator position,
  266. std::list<T>& out_list, const_list_iterator element) {
  267. in_list.splice(position, out_list, element);
  268. }
  269. static const_list_iterator ListIterateTo(const std::list<T>& list, const T& element) {
  270. auto it = list.cbegin();
  271. while (it != list.cend() && *it != element) {
  272. ++it;
  273. }
  274. return it;
  275. }
  276. std::array<std::list<T>, Depth> levels;
  277. u64 used_priorities = 0;
  278. };
  279. } // namespace Common