thread_queue_list.h 4.8 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. namespace Common {
  8. template <class T, unsigned int N>
  9. struct ThreadQueueList {
  10. // TODO(yuriks): If performance proves to be a problem, the std::deques can be replaced with
  11. // (dynamically resizable) circular buffers to remove their overhead when
  12. // inserting and popping.
  13. using Priority = unsigned int;
  14. // Number of priority levels. (Valid levels are [0..NUM_QUEUES).)
  15. static constexpr Priority NUM_QUEUES = N;
  16. ThreadQueueList() {
  17. first = nullptr;
  18. }
  19. // Only for debugging, returns priority level.
  20. [[nodiscard]] Priority contains(const T& uid) const {
  21. for (Priority i = 0; i < NUM_QUEUES; ++i) {
  22. const Queue& cur = queues[i];
  23. if (std::find(cur.data.cbegin(), cur.data.cend(), uid) != cur.data.cend()) {
  24. return i;
  25. }
  26. }
  27. return -1;
  28. }
  29. [[nodiscard]] T get_first() const {
  30. const Queue* cur = first;
  31. while (cur != nullptr) {
  32. if (!cur->data.empty()) {
  33. return cur->data.front();
  34. }
  35. cur = cur->next_nonempty;
  36. }
  37. return T();
  38. }
  39. template <typename UnaryPredicate>
  40. [[nodiscard]] T get_first_filter(UnaryPredicate filter) const {
  41. const Queue* cur = first;
  42. while (cur != nullptr) {
  43. if (!cur->data.empty()) {
  44. for (const auto& item : cur->data) {
  45. if (filter(item))
  46. return item;
  47. }
  48. }
  49. cur = cur->next_nonempty;
  50. }
  51. return T();
  52. }
  53. T pop_first() {
  54. Queue* cur = first;
  55. while (cur != nullptr) {
  56. if (!cur->data.empty()) {
  57. auto tmp = std::move(cur->data.front());
  58. cur->data.pop_front();
  59. return tmp;
  60. }
  61. cur = cur->next_nonempty;
  62. }
  63. return T();
  64. }
  65. T pop_first_better(Priority priority) {
  66. Queue* cur = first;
  67. Queue* stop = &queues[priority];
  68. while (cur < stop) {
  69. if (!cur->data.empty()) {
  70. auto tmp = std::move(cur->data.front());
  71. cur->data.pop_front();
  72. return tmp;
  73. }
  74. cur = cur->next_nonempty;
  75. }
  76. return T();
  77. }
  78. void push_front(Priority priority, const T& thread_id) {
  79. Queue* cur = &queues[priority];
  80. cur->data.push_front(thread_id);
  81. }
  82. void push_back(Priority priority, const T& thread_id) {
  83. Queue* cur = &queues[priority];
  84. cur->data.push_back(thread_id);
  85. }
  86. void move(const T& thread_id, Priority old_priority, Priority new_priority) {
  87. remove(old_priority, thread_id);
  88. prepare(new_priority);
  89. push_back(new_priority, thread_id);
  90. }
  91. void remove(Priority priority, const T& thread_id) {
  92. Queue* const cur = &queues[priority];
  93. const auto iter = std::remove(cur->data.begin(), cur->data.end(), thread_id);
  94. cur->data.erase(iter, cur->data.end());
  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. [[nodiscard]] 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