summaryrefslogtreecommitdiff
path: root/src/common/thread_queue_list.h
diff options
context:
space:
mode:
authorGravatar bunnei2015-01-07 17:58:31 -0500
committerGravatar bunnei2015-01-07 17:58:31 -0500
commite6864a1f411d35cf2a7ccfb5d3e44b717b1e2985 (patch)
tree4388fcda5083334eee3a59cb1a18e820a0960b1a /src/common/thread_queue_list.h
parentMerge pull request #442 from lioncash/smul (diff)
parentCommon: Clean up ThreadQueueList (diff)
downloadyuzu-e6864a1f411d35cf2a7ccfb5d3e44b717b1e2985.tar.gz
yuzu-e6864a1f411d35cf2a7ccfb5d3e44b717b1e2985.tar.xz
yuzu-e6864a1f411d35cf2a7ccfb5d3e44b717b1e2985.zip
Merge pull request #431 from yuriks/thread-queue-cleanup
Common: Clean up ThreadQueueList
Diffstat (limited to 'src/common/thread_queue_list.h')
-rw-r--r--src/common/thread_queue_list.h218
1 files changed, 74 insertions, 144 deletions
diff --git a/src/common/thread_queue_list.h b/src/common/thread_queue_list.h
index 4e1c0a215..444abf115 100644
--- a/src/common/thread_queue_list.h
+++ b/src/common/thread_queue_list.h
@@ -4,213 +4,143 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array>
8#include <deque>
9
10#include <boost/range/algorithm_ext/erase.hpp>
11
7#include "common/common.h" 12#include "common/common.h"
8 13
9namespace Common { 14namespace Common {
10 15
11template<class IdType> 16template<class T, unsigned int N>
12struct ThreadQueueList { 17struct ThreadQueueList {
13 // Number of queues (number of priority levels starting at 0.) 18 // TODO(yuriks): If performance proves to be a problem, the std::deques can be replaced with
14 static const int NUM_QUEUES = 128; 19 // (dynamically resizable) circular buffers to remove their overhead when
20 // inserting and popping.
15 21
16 // Initial number of threads a single queue can handle. 22 typedef unsigned int Priority;
17 static const int INITIAL_CAPACITY = 32;
18 23
19 struct Queue { 24 // Number of priority levels. (Valid levels are [0..NUM_QUEUES).)
20 // Next ever-been-used queue (worse priority.) 25 static const Priority NUM_QUEUES = N;
21 Queue *next;
22 // First valid item in data.
23 int first;
24 // One after last valid item in data.
25 int end;
26 // A too-large array with room on the front and end.
27 IdType *data;
28 // Size of data array.
29 int capacity;
30 };
31 26
32 ThreadQueueList() { 27 ThreadQueueList() {
33 memset(queues, 0, sizeof(queues)); 28 first = nullptr;
34 first = invalid();
35 }
36
37 ~ThreadQueueList() {
38 for (int i = 0; i < NUM_QUEUES; ++i)
39 {
40 if (queues[i].data != nullptr)
41 free(queues[i].data);
42 }
43 } 29 }
44 30
45 // Only for debugging, returns priority level. 31 // Only for debugging, returns priority level.
46 int contains(const IdType uid) { 32 Priority contains(const T& uid) {
47 for (int i = 0; i < NUM_QUEUES; ++i) 33 for (Priority i = 0; i < NUM_QUEUES; ++i) {
48 { 34 Queue& cur = queues[i];
49 if (queues[i].data == nullptr) 35 if (std::find(cur.data.cbegin(), cur.data.cend(), uid) != cur.data.cend()) {
50 continue; 36 return i;
51
52 Queue *cur = &queues[i];
53 for (int j = cur->first; j < cur->end; ++j)
54 {
55 if (cur->data[j] == uid)
56 return i;
57 } 37 }
58 } 38 }
59 39
60 return -1; 40 return -1;
61 } 41 }
62 42
63 inline IdType pop_first() { 43 T pop_first() {
64 Queue *cur = first; 44 Queue *cur = first;
65 while (cur != invalid()) 45 while (cur != nullptr) {
66 { 46 if (!cur->data.empty()) {
67 if (cur->end - cur->first > 0) 47 auto tmp = std::move(cur->data.front());
68 return cur->data[cur->first++]; 48 cur->data.pop_front();
69 cur = cur->next; 49 return tmp;
50 }
51 cur = cur->next_nonempty;
70 } 52 }
71 53
72 //_dbg_assert_msg_(SCEKERNEL, false, "ThreadQueueList should not be empty."); 54 return T();
73 return 0;
74 } 55 }
75 56
76 inline IdType pop_first_better(u32 priority) { 57 T pop_first_better(Priority priority) {
77 Queue *cur = first; 58 Queue *cur = first;
78 Queue *stop = &queues[priority]; 59 Queue *stop = &queues[priority];
79 while (cur < stop) 60 while (cur < stop) {
80 { 61 if (!cur->data.empty()) {
81 if (cur->end - cur->first > 0) 62 auto tmp = std::move(cur->data.front());
82 return cur->data[cur->first++]; 63 cur->data.pop_front();
83 cur = cur->next; 64 return tmp;
65 }
66 cur = cur->next_nonempty;
84 } 67 }
85 68
86 return 0; 69 return T();
87 } 70 }
88 71
89 inline void push_front(u32 priority, const IdType threadID) { 72 void push_front(Priority priority, const T& thread_id) {
90 Queue *cur = &queues[priority]; 73 Queue *cur = &queues[priority];
91 cur->data[--cur->first] = threadID; 74 cur->data.push_front(thread_id);
92 if (cur->first == 0)
93 rebalance(priority);
94 } 75 }
95 76
96 inline void push_back(u32 priority, const IdType threadID) { 77 void push_back(Priority priority, const T& thread_id) {
97 Queue *cur = &queues[priority]; 78 Queue *cur = &queues[priority];
98 cur->data[cur->end++] = threadID; 79 cur->data.push_back(thread_id);
99 if (cur->end == cur->capacity)
100 rebalance(priority);
101 } 80 }
102 81
103 inline void remove(u32 priority, const IdType threadID) { 82 void remove(Priority priority, const T& thread_id) {
104 Queue *cur = &queues[priority]; 83 Queue *cur = &queues[priority];
105 //_dbg_assert_msg_(SCEKERNEL, cur->next != NULL, "ThreadQueueList::Queue should already be linked up."); 84 boost::remove_erase(cur->data, thread_id);
106
107 for (int i = cur->first; i < cur->end; ++i)
108 {
109 if (cur->data[i] == threadID)
110 {
111 int remaining = --cur->end - i;
112 if (remaining > 0)
113 memmove(&cur->data[i], &cur->data[i + 1], remaining * sizeof(IdType));
114 return;
115 }
116 }
117
118 // Wasn't there.
119 } 85 }
120 86
121 inline void rotate(u32 priority) { 87 void rotate(Priority priority) {
122 Queue *cur = &queues[priority]; 88 Queue *cur = &queues[priority];
123 //_dbg_assert_msg_(SCEKERNEL, cur->next != NULL, "ThreadQueueList::Queue should already be linked up.");
124 89
125 if (cur->end - cur->first > 1) 90 if (cur->data.size() > 1) {
126 { 91 cur->data.push_back(std::move(cur->data.front()));
127 cur->data[cur->end++] = cur->data[cur->first++]; 92 cur->data.pop_front();
128 if (cur->end == cur->capacity)
129 rebalance(priority);
130 } 93 }
131 } 94 }
132 95
133 inline void clear() { 96 void clear() {
134 for (int i = 0; i < NUM_QUEUES; ++i) 97 queues.fill(Queue());
135 { 98 first = nullptr;
136 if (queues[i].data != nullptr)
137 free(queues[i].data);
138 }
139 memset(queues, 0, sizeof(queues));
140 first = invalid();
141 } 99 }
142 100
143 inline bool empty(u32 priority) const { 101 bool empty(Priority priority) const {
144 const Queue *cur = &queues[priority]; 102 const Queue *cur = &queues[priority];
145 return cur->first == cur->end; 103 return cur->data.empty();
146 } 104 }
147 105
148 inline void prepare(u32 priority) { 106 void prepare(Priority priority) {
149 Queue *cur = &queues[priority]; 107 Queue* cur = &queues[priority];
150 if (cur->next == nullptr) 108 if (cur->next_nonempty == UnlinkedTag())
151 link(priority, INITIAL_CAPACITY); 109 link(priority);
152 } 110 }
153 111
154private: 112private:
155 Queue *invalid() const { 113 struct Queue {
156 return (Queue *) -1; 114 // Points to the next active priority, skipping over ones that have never been used.
115 Queue* next_nonempty = UnlinkedTag();
116 // Double-ended queue of threads in this priority level
117 std::deque<T> data;
118 };
119
120 /// Special tag used to mark priority levels that have never been used.
121 static Queue* UnlinkedTag() {
122 return reinterpret_cast<Queue*>(1);
157 } 123 }
158 124
159 void link(u32 priority, int size) { 125 void link(Priority priority) {
160 //_dbg_assert_msg_(SCEKERNEL, queues[priority].data == NULL, "ThreadQueueList::Queue should only be initialized once.");
161
162 if (size <= INITIAL_CAPACITY)
163 size = INITIAL_CAPACITY;
164 else
165 {
166 int goal = size;
167 size = INITIAL_CAPACITY;
168 while (size < goal)
169 size *= 2;
170 }
171 Queue *cur = &queues[priority]; 126 Queue *cur = &queues[priority];
172 cur->data = (IdType *) malloc(sizeof(IdType) * size); 127
173 cur->capacity = size; 128 for (int i = priority - 1; i >= 0; --i) {
174 cur->first = size / 2; 129 if (queues[i].next_nonempty != UnlinkedTag()) {
175 cur->end = size / 2; 130 cur->next_nonempty = queues[i].next_nonempty;
176 131 queues[i].next_nonempty = cur;
177 for (int i = (int) priority - 1; i >= 0; --i)
178 {
179 if (queues[i].next != nullptr)
180 {
181 cur->next = queues[i].next;
182 queues[i].next = cur;
183 return; 132 return;
184 } 133 }
185 } 134 }
186 135
187 cur->next = first; 136 cur->next_nonempty = first;
188 first = cur; 137 first = cur;
189 } 138 }
190 139
191 void rebalance(u32 priority) {
192 Queue *cur = &queues[priority];
193 int size = cur->end - cur->first;
194 if (size >= cur->capacity - 2) {
195 IdType *new_data = (IdType *)realloc(cur->data, cur->capacity * 2 * sizeof(IdType));
196 if (new_data != nullptr) {
197 cur->capacity *= 2;
198 cur->data = new_data;
199 }
200 }
201
202 int newFirst = (cur->capacity - size) / 2;
203 if (newFirst != cur->first) {
204 memmove(&cur->data[newFirst], &cur->data[cur->first], size * sizeof(IdType));
205 cur->first = newFirst;
206 cur->end = newFirst + size;
207 }
208 }
209
210 // The first queue that's ever been used. 140 // The first queue that's ever been used.
211 Queue *first; 141 Queue* first;
212 // The priority level queues of thread ids. 142 // The priority level queues of thread ids.
213 Queue queues[NUM_QUEUES]; 143 std::array<Queue, NUM_QUEUES> queues;
214}; 144};
215 145
216} // namespace 146} // namespace