thread_queue_list.h 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  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. #include "common/common.h"
  9. namespace Common {
  10. template<class T, unsigned int N>
  11. struct ThreadQueueList {
  12. // TODO(yuriks): If performance proves to be a problem, the std::deques can be replaced with
  13. // (dynamically resizable) circular buffers to remove their overhead when
  14. // inserting and popping.
  15. typedef unsigned int Priority;
  16. // Number of priority levels. (Valid levels are [0..NUM_QUEUES).)
  17. static const Priority NUM_QUEUES = N;
  18. ThreadQueueList() {
  19. first = nullptr;
  20. }
  21. // Only for debugging, returns priority level.
  22. Priority contains(const T& uid) {
  23. for (Priority i = 0; i < NUM_QUEUES; ++i) {
  24. Queue& cur = queues[i];
  25. if (std::find(cur.data.cbegin(), cur.data.cend(), uid) != cur.data.cend()) {
  26. return i;
  27. }
  28. }
  29. return -1;
  30. }
  31. T pop_first() {
  32. Queue *cur = first;
  33. while (cur != nullptr) {
  34. if (!cur->data.empty()) {
  35. auto tmp = std::move(cur->data.front());
  36. cur->data.pop_front();
  37. return tmp;
  38. }
  39. cur = cur->next_nonempty;
  40. }
  41. return T();
  42. }
  43. T pop_first_better(Priority priority) {
  44. Queue *cur = first;
  45. Queue *stop = &queues[priority];
  46. while (cur < stop) {
  47. if (!cur->data.empty()) {
  48. auto tmp = std::move(cur->data.front());
  49. cur->data.pop_front();
  50. return tmp;
  51. }
  52. cur = cur->next_nonempty;
  53. }
  54. return T();
  55. }
  56. void push_front(Priority priority, const T& thread_id) {
  57. Queue *cur = &queues[priority];
  58. cur->data.push_front(thread_id);
  59. }
  60. void push_back(Priority priority, const T& thread_id) {
  61. Queue *cur = &queues[priority];
  62. cur->data.push_back(thread_id);
  63. }
  64. void remove(Priority priority, const T& thread_id) {
  65. Queue *cur = &queues[priority];
  66. boost::remove_erase(cur->data, thread_id);
  67. }
  68. void rotate(Priority priority) {
  69. Queue *cur = &queues[priority];
  70. if (cur->data.size() > 1) {
  71. cur->data.push_back(std::move(cur->data.front()));
  72. cur->data.pop_front();
  73. }
  74. }
  75. void clear() {
  76. queues.fill(Queue());
  77. first = nullptr;
  78. }
  79. bool empty(Priority priority) const {
  80. const Queue *cur = &queues[priority];
  81. return cur->data.empty();
  82. }
  83. void prepare(Priority priority) {
  84. Queue* cur = &queues[priority];
  85. if (cur->next_nonempty == UnlinkedTag())
  86. link(priority);
  87. }
  88. private:
  89. struct Queue {
  90. // Points to the next active priority, skipping over ones that have never been used.
  91. Queue* next_nonempty = UnlinkedTag();
  92. // Double-ended queue of threads in this priority level
  93. std::deque<T> data;
  94. };
  95. /// Special tag used to mark priority levels that have never been used.
  96. static Queue* UnlinkedTag() {
  97. return reinterpret_cast<Queue*>(1);
  98. }
  99. void link(Priority priority) {
  100. Queue *cur = &queues[priority];
  101. for (int i = priority - 1; i >= 0; --i) {
  102. if (queues[i].next_nonempty != UnlinkedTag()) {
  103. cur->next_nonempty = queues[i].next_nonempty;
  104. queues[i].next_nonempty = cur;
  105. return;
  106. }
  107. }
  108. cur->next_nonempty = first;
  109. first = cur;
  110. }
  111. // The first queue that's ever been used.
  112. Queue* first;
  113. // The priority level queues of thread ids.
  114. std::array<Queue, NUM_QUEUES> queues;
  115. };
  116. } // namespace