CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/HLE/ThreadQueueList.h
Views: 1401
1
// Copyright (c) 2012- PPSSPP Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
#pragma once
19
20
#include "Core/HLE/sceKernel.h"
21
#include "Common/Serialize/Serializer.h"
22
23
struct ThreadQueueList {
24
// Number of queues (number of priority levels starting at 0.)
25
static const int NUM_QUEUES = 128;
26
// Initial number of threads a single queue can handle.
27
static const int INITIAL_CAPACITY = 32;
28
29
struct Queue {
30
// Next ever-been-used queue (worse priority.)
31
Queue *next;
32
// First valid item in data.
33
int first;
34
// One after last valid item in data.
35
int end;
36
// A too-large array with room on the front and end.
37
SceUID *data;
38
// Size of data array.
39
int capacity;
40
41
inline int size() const {
42
return end - first;
43
}
44
inline bool empty() const {
45
return first == end;
46
}
47
inline int full() const {
48
return end == capacity;
49
}
50
};
51
52
ThreadQueueList() {
53
memset(queues, 0, sizeof(queues));
54
first = invalid();
55
}
56
57
~ThreadQueueList() {
58
clear();
59
}
60
61
// Only for debugging, returns priority level.
62
int contains(const SceUID uid) {
63
for (int i = 0; i < NUM_QUEUES; ++i) {
64
if (queues[i].data == nullptr)
65
continue;
66
67
Queue *cur = &queues[i];
68
for (int j = cur->first; j < cur->end; ++j) {
69
if (cur->data[j] == uid)
70
return i;
71
}
72
}
73
74
return -1;
75
}
76
77
inline SceUID pop_first() {
78
Queue *cur = first;
79
while (cur != invalid()) {
80
if (cur->size() > 0)
81
return cur->data[cur->first++];
82
cur = cur->next;
83
}
84
85
_dbg_assert_msg_(false, "ThreadQueueList should not be empty.");
86
return 0;
87
}
88
89
inline SceUID pop_first_better(u32 priority) {
90
Queue *cur = first;
91
// Don't bother looking past (worse than) this priority.
92
Queue *stop = &queues[priority];
93
while (cur < stop) {
94
if (cur->size() > 0)
95
return cur->data[cur->first++];
96
cur = cur->next;
97
}
98
99
return 0;
100
}
101
102
inline SceUID peek_first() {
103
Queue *cur = first;
104
while (cur != invalid()) {
105
if (cur->size() > 0)
106
return cur->data[cur->first];
107
cur = cur->next;
108
}
109
110
return 0;
111
}
112
113
inline void push_front(u32 priority, const SceUID threadID) {
114
Queue *cur = &queues[priority];
115
cur->data[--cur->first] = threadID;
116
// If we ran out of room toward the front, add more room for next time.
117
if (cur->first == 0)
118
rebalance(priority);
119
}
120
121
inline void push_back(u32 priority, const SceUID threadID) {
122
Queue *cur = &queues[priority];
123
cur->data[cur->end++] = threadID;
124
if (cur->full())
125
rebalance(priority);
126
}
127
128
inline void remove(u32 priority, const SceUID threadID) {
129
Queue *cur = &queues[priority];
130
_dbg_assert_msg_(cur->next != nullptr, "ThreadQueueList::Queue should already be linked up.");
131
132
for (int i = cur->first; i < cur->end; ++i) {
133
if (cur->data[i] == threadID) {
134
// How many more after this one?
135
int remaining = cur->end - i;
136
// If there are more, move them into place.
137
if (remaining > 0)
138
memmove(&cur->data[i], &cur->data[i + 1], remaining * sizeof(SceUID));
139
140
// Now we're one shorter.
141
--cur->end;
142
return;
143
}
144
}
145
146
// Wasn't there.
147
}
148
149
inline void rotate(u32 priority) {
150
Queue *cur = &queues[priority];
151
_dbg_assert_msg_(cur->next != nullptr, "ThreadQueueList::Queue should already be linked up.");
152
153
if (cur->size() > 1) {
154
// Grab the front and push it on the end.
155
cur->data[cur->end++] = cur->data[cur->first++];
156
if (cur->full())
157
rebalance(priority);
158
}
159
}
160
161
inline void clear() {
162
for (int i = 0; i < NUM_QUEUES; ++i) {
163
free(queues[i].data);
164
}
165
memset(queues, 0, sizeof(queues));
166
first = invalid();
167
}
168
169
inline bool empty(u32 priority) const {
170
const Queue *cur = &queues[priority];
171
return cur->empty();
172
}
173
174
inline void prepare(u32 priority) {
175
Queue *cur = &queues[priority];
176
if (cur->next == nullptr)
177
link(priority, INITIAL_CAPACITY);
178
}
179
180
void DoState(PointerWrap &p) {
181
auto s = p.Section("ThreadQueueList", 1);
182
if (!s)
183
return;
184
185
int numQueues = NUM_QUEUES;
186
Do(p, numQueues);
187
if (numQueues != NUM_QUEUES) {
188
p.SetError(p.ERROR_FAILURE);
189
ERROR_LOG(Log::sceKernel, "Savestate loading error: invalid data");
190
return;
191
}
192
193
if (p.mode == p.MODE_READ)
194
clear();
195
196
for (int i = 0; i < NUM_QUEUES; ++i) {
197
Queue *cur = &queues[i];
198
int size = cur->size();
199
Do(p, size);
200
int capacity = cur->capacity;
201
Do(p, capacity);
202
203
if (capacity == 0)
204
continue;
205
206
if (p.mode == p.MODE_READ) {
207
link(i, capacity);
208
cur->first = (cur->capacity - size) / 2;
209
cur->end = cur->first + size;
210
}
211
212
if (size != 0)
213
DoArray(p, &cur->data[cur->first], size);
214
}
215
}
216
217
private:
218
Queue *invalid() const {
219
return (Queue *)-1;
220
}
221
222
// Initialize a priority level and link to other queues.
223
void link(u32 priority, int size) {
224
_dbg_assert_msg_(queues[priority].data == nullptr, "ThreadQueueList::Queue should only be initialized once.");
225
226
// Make sure we stay a multiple of INITIAL_CAPACITY.
227
if (size <= INITIAL_CAPACITY)
228
size = INITIAL_CAPACITY;
229
else {
230
int goal = size;
231
size = INITIAL_CAPACITY;
232
while (size < goal)
233
size *= 2;
234
}
235
236
// Allocate the queue.
237
Queue *cur = &queues[priority];
238
cur->data = (SceUID *)malloc(sizeof(SceUID) * size);
239
cur->capacity = size;
240
// Start smack in the middle so it can move both directions.
241
cur->first = size / 2;
242
cur->end = size / 2;
243
244
for (int i = (int)priority - 1; i >= 0; --i) {
245
// This queue is before ours, and points past us.
246
// We'll have it point to our new queue, inserting into the chain.
247
if (queues[i].next != nullptr) {
248
cur->next = queues[i].next;
249
queues[i].next = cur;
250
return;
251
}
252
}
253
254
// Never found above - that means there's no better queue yet.
255
// The new one is now first, and whoever was first is after it.
256
cur->next = first;
257
first = cur;
258
}
259
260
// Move or allocate as necessary to maintain free space on both sides.
261
void rebalance(u32 priority) {
262
Queue *cur = &queues[priority];
263
int size = cur->size();
264
// Basically full. Time for a larger queue?
265
if (size >= cur->capacity - 2) {
266
int new_capacity = cur->capacity * 2;
267
SceUID *new_data = (SceUID *)realloc(cur->data, new_capacity * sizeof(SceUID));
268
if (new_data != nullptr) {
269
// Success, it's bigger now.
270
cur->capacity = new_capacity;
271
cur->data = new_data;
272
}
273
}
274
275
// If we center all the items, it should start here.
276
int newFirst = (cur->capacity - size) / 2;
277
if (newFirst != cur->first) {
278
memmove(&cur->data[newFirst], &cur->data[cur->first], size * sizeof(SceUID));
279
cur->first = newFirst;
280
cur->end = newFirst + size;
281
}
282
}
283
284
// The first queue that's ever been used.
285
Queue *first;
286
// The priority level queues of thread ids.
287
Queue queues[NUM_QUEUES];
288
};
289
290