thread_queue_list.h 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. // Copyright 2014 Citra Emulator Project / PPSSPP Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #pragma once
  5. #include <array>
  6. #include <deque>
  7. #include <boost/range/algorithm_ext/erase.hpp>
  8. namespace Common {
  9. template <class T, unsigned int N>
  10. struct ThreadQueueList {
  11. // TODO(yuriks): If performance proves to be a problem, the std::deques can be replaced with
  12. // (dynamically resizable) circular buffers to remove their overhead when
  13. // inserting and popping.
  14. using Priority = unsigned int;
  15. // Number of priority levels. (Valid levels are [0..NUM_QUEUES).)
  16. static const Priority NUM_QUEUES = N;
  17. ThreadQueueList() {
  18. first = nullptr;
  19. }
  20. // Only for debugging, returns priority level.
  21. Priority contains(const T& uid) const {
  22. for (Priority i = 0; i < NUM_QUEUES; ++i) {
  23. const Queue& cur = queues[i];
  24. if (std::find(cur.data.cbegin(), cur.data.cend(), uid) != cur.data.cend()) {
  25. return i;
  26. }
  27. }
  28. return -1;
  29. }
  30. T get_first() const {
  31. const Queue* cur = first;
  32. while (cur != nullptr) {
  33. if (!cur->data.empty()) {
  34. return cur->data.front();
  35. }
  36. cur = cur->next_nonempty;
  37. }
  38. return T();
  39. }
  40. template <typename UnaryPredicate>
  41. T get_first_filter(UnaryPredicate filter) const {
  42. const Queue* cur = first;
  43. while (cur != nullptr) {
  44. if (!cur->data.empty()) {
  45. for (const auto& item : cur->data) {
  46. if (filter(item))
  47. return item;
  48. }
  49. }
  50. cur = cur->next_nonempty;
  51. }
  52. return T();
  53. }
  54. T pop_first() {
  55. Queue* cur = first;
  56. while (cur != nullptr) {
  57. if (!cur->data.empty()) {
  58. auto tmp = std::move(cur->data.front());
  59. cur->data.pop_front();
  60. return tmp;
  61. }
  62. cur = cur->next_nonempty;
  63. }
  64. return T();
  65. }
  66. T pop_first_better(Priority priority) {
  67. Queue* cur = first;
  68. Queue* stop = &queues[priority];
  69. while (cur < stop) {
  70. if (!cur->data.empty()) {
  71. auto tmp = std::move(cur->data.front());
  72. cur->data.pop_front();
  73. return tmp;
  74. }
  75. cur = cur->next_nonempty;
  76. }
  77. return T();
  78. }
  79. void push_front(Priority priority, const T& thread_id) {
  80. Queue* cur = &queues[priority];
  81. cur->data.push_front(thread_id);
  82. }
  83. void push_back(Priority priority, const T& thread_id) {
  84. Queue* cur = &queues[priority];
  85. cur->data.push_back(thread_id);
  86. }
  87. void move(const T& thread_id, Priority old_priority, Priority new_priority) {
  88. remove(old_priority, thread_id);
  89. prepare(new_priority);
  90. push_back(new_priority, thread_id);
  91. }
  92. void remove(Priority priority, const T& thread_id) {
  93. Queue* cur = &queues[priority];
  94. boost::remove_erase(cur->data, thread_id);
  95. }
  96. void rotate(Priority priority) {
  97. Queue* cur = &queues[priority];
  98. if (cur->data.size() > 1) {
  99. cur->data.push_back(std::move(cur->data.front()));
  100. cur->data.pop_front();
  101. }
  102. }
  103. void clear() {
  104. queues.fill(Queue());
  105. first = nullptr;
  106. }
  107. bool empty(Priority priority) const {
  108. const Queue* cur = &queues[priority];
  109. return cur->data.empty();
  110. }
  111. void prepare(Priority priority) {
  112. Queue* cur = &queues[priority];
  113. if (cur->next_nonempty == UnlinkedTag())
  114. link(priority);
  115. }
  116. private:
  117. struct Queue {
  118. // Points to the next active priority, skipping over ones that have never been used.
  119. Queue* next_nonempty = UnlinkedTag();
  120. // Double-ended queue of threads in this priority level
  121. std::deque<T> data;
  122. };
  123. /// Special tag used to mark priority levels that have never been used.
  124. static Queue* UnlinkedTag() {
  125. return reinterpret_cast<Queue*>(1);
  126. }
  127. void link(Priority priority) {
  128. Queue* cur = &queues[priority];
  129. for (int i = priority - 1; i >= 0; --i) {
  130. if (queues[i].next_nonempty != UnlinkedTag()) {
  131. cur->next_nonempty = queues[i].next_nonempty;
  132. queues[i].next_nonempty = cur;
  133. return;
  134. }
  135. }
  136. cur->next_nonempty = first;
  137. first = cur;
  138. }
  139. // The first queue that's ever been used.
  140. Queue* first;
  141. // The priority level queues of thread ids.
  142. std::array<Queue, NUM_QUEUES> queues;
  143. };
  144. } // namespace Common