thread_queue_list.h 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  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. typedef unsigned int Priority;
  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) {
  22. for (Priority i = 0; i < NUM_QUEUES; ++i) {
  23. 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() {
  31. 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. T pop_first() {
  41. Queue* cur = first;
  42. while (cur != nullptr) {
  43. if (!cur->data.empty()) {
  44. auto tmp = std::move(cur->data.front());
  45. cur->data.pop_front();
  46. return tmp;
  47. }
  48. cur = cur->next_nonempty;
  49. }
  50. return T();
  51. }
  52. T pop_first_better(Priority priority) {
  53. Queue* cur = first;
  54. Queue* stop = &queues[priority];
  55. while (cur < stop) {
  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. void push_front(Priority priority, const T& thread_id) {
  66. Queue* cur = &queues[priority];
  67. cur->data.push_front(thread_id);
  68. }
  69. void push_back(Priority priority, const T& thread_id) {
  70. Queue* cur = &queues[priority];
  71. cur->data.push_back(thread_id);
  72. }
  73. void move(const T& thread_id, Priority old_priority, Priority new_priority) {
  74. remove(old_priority, thread_id);
  75. prepare(new_priority);
  76. push_back(new_priority, thread_id);
  77. }
  78. void remove(Priority priority, const T& thread_id) {
  79. Queue* cur = &queues[priority];
  80. boost::remove_erase(cur->data, thread_id);
  81. }
  82. void rotate(Priority priority) {
  83. Queue* cur = &queues[priority];
  84. if (cur->data.size() > 1) {
  85. cur->data.push_back(std::move(cur->data.front()));
  86. cur->data.pop_front();
  87. }
  88. }
  89. void clear() {
  90. queues.fill(Queue());
  91. first = nullptr;
  92. }
  93. bool empty(Priority priority) const {
  94. const Queue* cur = &queues[priority];
  95. return cur->data.empty();
  96. }
  97. void prepare(Priority priority) {
  98. Queue* cur = &queues[priority];
  99. if (cur->next_nonempty == UnlinkedTag())
  100. link(priority);
  101. }
  102. private:
  103. struct Queue {
  104. // Points to the next active priority, skipping over ones that have never been used.
  105. Queue* next_nonempty = UnlinkedTag();
  106. // Double-ended queue of threads in this priority level
  107. std::deque<T> data;
  108. };
  109. /// Special tag used to mark priority levels that have never been used.
  110. static Queue* UnlinkedTag() {
  111. return reinterpret_cast<Queue*>(1);
  112. }
  113. void link(Priority priority) {
  114. Queue* cur = &queues[priority];
  115. for (int i = priority - 1; i >= 0; --i) {
  116. if (queues[i].next_nonempty != UnlinkedTag()) {
  117. cur->next_nonempty = queues[i].next_nonempty;
  118. queues[i].next_nonempty = cur;
  119. return;
  120. }
  121. }
  122. cur->next_nonempty = first;
  123. first = cur;
  124. }
  125. // The first queue that's ever been used.
  126. Queue* first;
  127. // The priority level queues of thread ids.
  128. std::array<Queue, NUM_QUEUES> queues;
  129. };
  130. } // namespace Common