summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar bunnei2020-11-28 11:23:36 -0800
committerGravatar bunnei2020-12-06 00:03:24 -0800
commita3ccac3eb7f6feb87ca7026c89531a0afd5fabab (patch)
tree2ab1554d45aa6d4835acbafb3dbf370a54e683a4 /src
parentcommon: Port BitSet from Mesosphere. (diff)
downloadyuzu-a3ccac3eb7f6feb87ca7026c89531a0afd5fabab.tar.gz
yuzu-a3ccac3eb7f6feb87ca7026c89531a0afd5fabab.tar.xz
yuzu-a3ccac3eb7f6feb87ca7026c89531a0afd5fabab.zip
common: Port KPriorityQueue from Mesosphere.
Diffstat (limited to 'src')
-rw-r--r--src/core/CMakeLists.txt1
-rw-r--r--src/core/hle/kernel/k_priority_queue.h443
2 files changed, 444 insertions, 0 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index adabe8dbd..4de159119 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -153,6 +153,7 @@ add_library(core STATIC
153 hle/kernel/hle_ipc.cpp 153 hle/kernel/hle_ipc.cpp
154 hle/kernel/hle_ipc.h 154 hle/kernel/hle_ipc.h
155 hle/kernel/k_affinity_mask.h 155 hle/kernel/k_affinity_mask.h
156 hle/kernel/k_priority_queue.h
156 hle/kernel/kernel.cpp 157 hle/kernel/kernel.cpp
157 hle/kernel/kernel.h 158 hle/kernel/kernel.h
158 hle/kernel/memory/address_space_info.cpp 159 hle/kernel/memory/address_space_info.cpp
diff --git a/src/core/hle/kernel/k_priority_queue.h b/src/core/hle/kernel/k_priority_queue.h
new file mode 100644
index 000000000..88e4e1ed4
--- /dev/null
+++ b/src/core/hle/kernel/k_priority_queue.h
@@ -0,0 +1,443 @@
1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5// This file references various implementation details from Atmosphere, an open-source firmware for
6// the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
7
8#pragma once
9
10#include "common/assert.h"
11#include "common/bit_set.h"
12#include "common/bit_util.h"
13#include "common/common_types.h"
14
15namespace Kernel {
16
17class Thread;
18
19template <typename T>
20concept KPriorityQueueAffinityMask = !std::is_reference<T>::value && requires(T & t) {
21 { t.GetAffinityMask() }
22 ->std::convertible_to<u64>;
23 {t.SetAffinityMask(std::declval<u64>())};
24
25 { t.GetAffinity(std::declval<int32_t>()) }
26 ->std::same_as<bool>;
27 {t.SetAffinity(std::declval<int32_t>(), std::declval<bool>())};
28 {t.SetAll()};
29};
30
31template <typename T>
32concept KPriorityQueueMember = !std::is_reference<T>::value && requires(T & t) {
33 {typename T::QueueEntry()};
34 {(typename T::QueueEntry()).Initialize()};
35 {(typename T::QueueEntry()).SetPrev(std::addressof(t))};
36 {(typename T::QueueEntry()).SetNext(std::addressof(t))};
37 { (typename T::QueueEntry()).GetNext() }
38 ->std::same_as<T*>;
39 { (typename T::QueueEntry()).GetPrev() }
40 ->std::same_as<T*>;
41 { t.GetPriorityQueueEntry(std::declval<s32>()) }
42 ->std::same_as<typename T::QueueEntry&>;
43
44 {t.GetAffinityMask()};
45 { typename std::remove_cvref<decltype(t.GetAffinityMask())>::type() }
46 ->KPriorityQueueAffinityMask;
47
48 { t.GetActiveCore() }
49 ->std::convertible_to<s32>;
50 { t.GetPriority() }
51 ->std::convertible_to<s32>;
52};
53
54template <typename Member, size_t _NumCores, int LowestPriority, int HighestPriority>
55requires KPriorityQueueMember<Member> class KPriorityQueue {
56public:
57 using AffinityMaskType = typename std::remove_cv<typename std::remove_reference<decltype(
58 std::declval<Member>().GetAffinityMask())>::type>::type;
59
60 static_assert(LowestPriority >= 0);
61 static_assert(HighestPriority >= 0);
62 static_assert(LowestPriority >= HighestPriority);
63 static constexpr size_t NumPriority = LowestPriority - HighestPriority + 1;
64 static constexpr size_t NumCores = _NumCores;
65
66 static constexpr bool IsValidCore(s32 core) {
67 return 0 <= core && core < static_cast<s32>(NumCores);
68 }
69
70 static constexpr bool IsValidPriority(s32 priority) {
71 return HighestPriority <= priority && priority <= LowestPriority + 1;
72 }
73
74private:
75 using Entry = typename Member::QueueEntry;
76
77public:
78 class KPerCoreQueue {
79 private:
80 Entry root[NumCores];
81
82 public:
83 constexpr KPerCoreQueue() : root() {
84 for (size_t i = 0; i < NumCores; i++) {
85 this->root[i].Initialize();
86 }
87 }
88
89 constexpr bool PushBack(s32 core, Member* member) {
90 /* Get the entry associated with the member. */
91 Entry& member_entry = member->GetPriorityQueueEntry(core);
92
93 /* Get the entry associated with the end of the queue. */
94 Member* tail = this->root[core].GetPrev();
95 Entry& tail_entry =
96 (tail != nullptr) ? tail->GetPriorityQueueEntry(core) : this->root[core];
97
98 /* Link the entries. */
99 member_entry.SetPrev(tail);
100 member_entry.SetNext(nullptr);
101 tail_entry.SetNext(member);
102 this->root[core].SetPrev(member);
103
104 return (tail == nullptr);
105 }
106
107 constexpr bool PushFront(s32 core, Member* member) {
108 /* Get the entry associated with the member. */
109 Entry& member_entry = member->GetPriorityQueueEntry(core);
110
111 /* Get the entry associated with the front of the queue. */
112 Member* head = this->root[core].GetNext();
113 Entry& head_entry =
114 (head != nullptr) ? head->GetPriorityQueueEntry(core) : this->root[core];
115
116 /* Link the entries. */
117 member_entry.SetPrev(nullptr);
118 member_entry.SetNext(head);
119 head_entry.SetPrev(member);
120 this->root[core].SetNext(member);
121
122 return (head == nullptr);
123 }
124
125 constexpr bool Remove(s32 core, Member* member) {
126 /* Get the entry associated with the member. */
127 Entry& member_entry = member->GetPriorityQueueEntry(core);
128
129 /* Get the entries associated with next and prev. */
130 Member* prev = member_entry.GetPrev();
131 Member* next = member_entry.GetNext();
132 Entry& prev_entry =
133 (prev != nullptr) ? prev->GetPriorityQueueEntry(core) : this->root[core];
134 Entry& next_entry =
135 (next != nullptr) ? next->GetPriorityQueueEntry(core) : this->root[core];
136
137 /* Unlink. */
138 prev_entry.SetNext(next);
139 next_entry.SetPrev(prev);
140
141 return (this->GetFront(core) == nullptr);
142 }
143
144 constexpr Member* GetFront(s32 core) const {
145 return this->root[core].GetNext();
146 }
147 };
148
149 class KPriorityQueueImpl {
150 private:
151 KPerCoreQueue queues[NumPriority];
152 Common::BitSet64<NumPriority> available_priorities[NumCores];
153
154 public:
155 constexpr KPriorityQueueImpl() : queues(), available_priorities() { /* ... */
156 }
157
158 constexpr void PushBack(s32 priority, s32 core, Member* member) {
159 ASSERT(IsValidCore(core));
160 ASSERT(IsValidPriority(priority));
161
162 if (priority <= LowestPriority) {
163 if (this->queues[priority].PushBack(core, member)) {
164 this->available_priorities[core].SetBit(priority);
165 }
166 }
167 }
168
169 constexpr void PushFront(s32 priority, s32 core, Member* member) {
170 ASSERT(IsValidCore(core));
171 ASSERT(IsValidPriority(priority));
172
173 if (priority <= LowestPriority) {
174 if (this->queues[priority].PushFront(core, member)) {
175 this->available_priorities[core].SetBit(priority);
176 }
177 }
178 }
179
180 constexpr void Remove(s32 priority, s32 core, Member* member) {
181 ASSERT(IsValidCore(core));
182 ASSERT(IsValidPriority(priority));
183
184 if (priority <= LowestPriority) {
185 if (this->queues[priority].Remove(core, member)) {
186 this->available_priorities[core].ClearBit(priority);
187 }
188 }
189 }
190
191 constexpr Member* GetFront(s32 core) const {
192 ASSERT(IsValidCore(core));
193
194 const s32 priority =
195 static_cast<s32>(this->available_priorities[core].CountLeadingZero());
196 if (priority <= LowestPriority) {
197 return this->queues[priority].GetFront(core);
198 } else {
199 return nullptr;
200 }
201 }
202
203 constexpr Member* GetFront(s32 priority, s32 core) const {
204 ASSERT(IsValidCore(core));
205 ASSERT(IsValidPriority(priority));
206
207 if (priority <= LowestPriority) {
208 return this->queues[priority].GetFront(core);
209 } else {
210 return nullptr;
211 }
212 }
213
214 constexpr Member* GetNext(s32 core, const Member* member) const {
215 ASSERT(IsValidCore(core));
216
217 Member* next = member->GetPriorityQueueEntry(core).GetNext();
218 if (next == nullptr) {
219 const s32 priority = static_cast<s32>(
220 this->available_priorities[core].GetNextSet(member->GetPriority()));
221 if (priority <= LowestPriority) {
222 next = this->queues[priority].GetFront(core);
223 }
224 }
225 return next;
226 }
227
228 constexpr void MoveToFront(s32 priority, s32 core, Member* member) {
229 ASSERT(IsValidCore(core));
230 ASSERT(IsValidPriority(priority));
231
232 if (priority <= LowestPriority) {
233 this->queues[priority].Remove(core, member);
234 this->queues[priority].PushFront(core, member);
235 }
236 }
237
238 constexpr Member* MoveToBack(s32 priority, s32 core, Member* member) {
239 ASSERT(IsValidCore(core));
240 ASSERT(IsValidPriority(priority));
241
242 if (priority <= LowestPriority) {
243 this->queues[priority].Remove(core, member);
244 this->queues[priority].PushBack(core, member);
245 return this->queues[priority].GetFront(core);
246 } else {
247 return nullptr;
248 }
249 }
250 };
251
252private:
253 KPriorityQueueImpl scheduled_queue;
254 KPriorityQueueImpl suggested_queue;
255
256private:
257 constexpr void ClearAffinityBit(u64& affinity, s32 core) {
258 affinity &= ~(u64(1ul) << core);
259 }
260
261 constexpr s32 GetNextCore(u64& affinity) {
262 const s32 core = Common::CountTrailingZeroes64(affinity);
263 ClearAffinityBit(affinity, core);
264 return core;
265 }
266
267 constexpr void PushBack(s32 priority, Member* member) {
268 ASSERT(IsValidPriority(priority));
269
270 /* Push onto the scheduled queue for its core, if we can. */
271 u64 affinity = member->GetAffinityMask().GetAffinityMask();
272 if (const s32 core = member->GetActiveCore(); core >= 0) {
273 this->scheduled_queue.PushBack(priority, core, member);
274 ClearAffinityBit(affinity, core);
275 }
276
277 /* And suggest the thread for all other cores. */
278 while (affinity) {
279 this->suggested_queue.PushBack(priority, GetNextCore(affinity), member);
280 }
281 }
282
283 constexpr void PushFront(s32 priority, Member* member) {
284 ASSERT(IsValidPriority(priority));
285
286 /* Push onto the scheduled queue for its core, if we can. */
287 u64 affinity = member->GetAffinityMask().GetAffinityMask();
288 if (const s32 core = member->GetActiveCore(); core >= 0) {
289 this->scheduled_queue.PushFront(priority, core, member);
290 ClearAffinityBit(affinity, core);
291 }
292
293 /* And suggest the thread for all other cores. */
294 /* Note: Nintendo pushes onto the back of the suggested queue, not the front. */
295 while (affinity) {
296 this->suggested_queue.PushBack(priority, GetNextCore(affinity), member);
297 }
298 }
299
300 constexpr void Remove(s32 priority, Member* member) {
301 ASSERT(IsValidPriority(priority));
302
303 /* Remove from the scheduled queue for its core. */
304 u64 affinity = member->GetAffinityMask().GetAffinityMask();
305 if (const s32 core = member->GetActiveCore(); core >= 0) {
306 this->scheduled_queue.Remove(priority, core, member);
307 ClearAffinityBit(affinity, core);
308 }
309
310 /* Remove from the suggested queue for all other cores. */
311 while (affinity) {
312 this->suggested_queue.Remove(priority, GetNextCore(affinity), member);
313 }
314 }
315
316public:
317 constexpr KPriorityQueue() : scheduled_queue(), suggested_queue() { /* ... */
318 }
319
320 /* Getters. */
321 constexpr Member* GetScheduledFront(s32 core) const {
322 return this->scheduled_queue.GetFront(core);
323 }
324
325 constexpr Member* GetScheduledFront(s32 core, s32 priority) const {
326 return this->scheduled_queue.GetFront(priority, core);
327 }
328
329 constexpr Member* GetSuggestedFront(s32 core) const {
330 return this->suggested_queue.GetFront(core);
331 }
332
333 constexpr Member* GetSuggestedFront(s32 core, s32 priority) const {
334 return this->suggested_queue.GetFront(priority, core);
335 }
336
337 constexpr Member* GetScheduledNext(s32 core, const Member* member) const {
338 return this->scheduled_queue.GetNext(core, member);
339 }
340
341 constexpr Member* GetSuggestedNext(s32 core, const Member* member) const {
342 return this->suggested_queue.GetNext(core, member);
343 }
344
345 constexpr Member* GetSamePriorityNext(s32 core, const Member* member) const {
346 return member->GetPriorityQueueEntry(core).GetNext();
347 }
348
349 /* Mutators. */
350 constexpr void PushBack(Member* member) {
351 this->PushBack(member->GetPriority(), member);
352 }
353
354 constexpr void Remove(Member* member) {
355 this->Remove(member->GetPriority(), member);
356 }
357
358 constexpr void MoveToScheduledFront(Member* member) {
359 this->scheduled_queue.MoveToFront(member->GetPriority(), member->GetActiveCore(), member);
360 }
361
362 constexpr Thread* MoveToScheduledBack(Member* member) {
363 return this->scheduled_queue.MoveToBack(member->GetPriority(), member->GetActiveCore(),
364 member);
365 }
366
367 /* First class fancy operations. */
368 constexpr void ChangePriority(s32 prev_priority, bool is_running, Member* member) {
369 ASSERT(IsValidPriority(prev_priority));
370
371 /* Remove the member from the queues. */
372 const s32 new_priority = member->GetPriority();
373 this->Remove(prev_priority, member);
374
375 /* And enqueue. If the member is running, we want to keep it running. */
376 if (is_running) {
377 this->PushFront(new_priority, member);
378 } else {
379 this->PushBack(new_priority, member);
380 }
381 }
382
383 constexpr void ChangeAffinityMask(s32 prev_core, const AffinityMaskType& prev_affinity,
384 Member* member) {
385 /* Get the new information. */
386 const s32 priority = member->GetPriority();
387 const AffinityMaskType& new_affinity = member->GetAffinityMask();
388 const s32 new_core = member->GetActiveCore();
389
390 /* Remove the member from all queues it was in before. */
391 for (s32 core = 0; core < static_cast<s32>(NumCores); core++) {
392 if (prev_affinity.GetAffinity(core)) {
393 if (core == prev_core) {
394 this->scheduled_queue.Remove(priority, core, member);
395 } else {
396 this->suggested_queue.Remove(priority, core, member);
397 }
398 }
399 }
400
401 /* And add the member to all queues it should be in now. */
402 for (s32 core = 0; core < static_cast<s32>(NumCores); core++) {
403 if (new_affinity.GetAffinity(core)) {
404 if (core == new_core) {
405 this->scheduled_queue.PushBack(priority, core, member);
406 } else {
407 this->suggested_queue.PushBack(priority, core, member);
408 }
409 }
410 }
411 }
412
413 constexpr void ChangeCore(s32 prev_core, Member* member, bool to_front = false) {
414 /* Get the new information. */
415 const s32 new_core = member->GetActiveCore();
416 const s32 priority = member->GetPriority();
417
418 /* We don't need to do anything if the core is the same. */
419 if (prev_core != new_core) {
420 /* Remove from the scheduled queue for the previous core. */
421 if (prev_core >= 0) {
422 this->scheduled_queue.Remove(priority, prev_core, member);
423 }
424
425 /* Remove from the suggested queue and add to the scheduled queue for the new core. */
426 if (new_core >= 0) {
427 this->suggested_queue.Remove(priority, new_core, member);
428 if (to_front) {
429 this->scheduled_queue.PushFront(priority, new_core, member);
430 } else {
431 this->scheduled_queue.PushBack(priority, new_core, member);
432 }
433 }
434
435 /* Add to the suggested queue for the previous core. */
436 if (prev_core >= 0) {
437 this->suggested_queue.PushBack(priority, prev_core, member);
438 }
439 }
440 }
441};
442
443} // namespace Kernel