diff options
| author | 2019-03-28 21:41:40 -0400 | |
|---|---|---|
| committer | 2019-03-28 21:41:40 -0400 | |
| commit | b404fcdf1443b91ac9994c05ad1fe039fcd9675e (patch) | |
| tree | 42b6141dcd6a5315f9330f064524963a7915c28a /src | |
| parent | Merge pull request #2284 from lioncash/heap-alloc (diff) | |
| parent | Fixes and corrections on formatting. (diff) | |
| download | yuzu-b404fcdf1443b91ac9994c05ad1fe039fcd9675e.tar.gz yuzu-b404fcdf1443b91ac9994c05ad1fe039fcd9675e.tar.xz yuzu-b404fcdf1443b91ac9994c05ad1fe039fcd9675e.zip | |
Merge pull request #2265 from FernandoS27/multilevelqueue
Replace old Thread Queue for a new Multi Level Queue
Diffstat (limited to '')
| -rw-r--r-- | src/common/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/common/bit_util.h | 39 | ||||
| -rw-r--r-- | src/common/multi_level_queue.h | 337 | ||||
| -rw-r--r-- | src/core/hle/kernel/scheduler.cpp | 42 | ||||
| -rw-r--r-- | src/core/hle/kernel/scheduler.h | 4 | ||||
| -rw-r--r-- | src/tests/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/tests/common/bit_utils.cpp | 23 | ||||
| -rw-r--r-- | src/tests/common/multi_level_queue.cpp | 55 |
8 files changed, 484 insertions, 19 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 43ae8a9e7..850ce8006 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -98,6 +98,7 @@ add_library(common STATIC | |||
| 98 | microprofile.h | 98 | microprofile.h |
| 99 | microprofileui.h | 99 | microprofileui.h |
| 100 | misc.cpp | 100 | misc.cpp |
| 101 | multi_level_queue.h | ||
| 101 | page_table.cpp | 102 | page_table.cpp |
| 102 | page_table.h | 103 | page_table.h |
| 103 | param_package.cpp | 104 | param_package.cpp |
diff --git a/src/common/bit_util.h b/src/common/bit_util.h index 1eea17ba1..a4f9ed4aa 100644 --- a/src/common/bit_util.h +++ b/src/common/bit_util.h | |||
| @@ -58,4 +58,43 @@ inline u64 CountLeadingZeroes64(u64 value) { | |||
| 58 | return __builtin_clzll(value); | 58 | return __builtin_clzll(value); |
| 59 | } | 59 | } |
| 60 | #endif | 60 | #endif |
| 61 | |||
| 62 | #ifdef _MSC_VER | ||
| 63 | inline u32 CountTrailingZeroes32(u32 value) { | ||
| 64 | unsigned long trailing_zero = 0; | ||
| 65 | |||
| 66 | if (_BitScanForward(&trailing_zero, value) != 0) { | ||
| 67 | return trailing_zero; | ||
| 68 | } | ||
| 69 | |||
| 70 | return 32; | ||
| 71 | } | ||
| 72 | |||
| 73 | inline u64 CountTrailingZeroes64(u64 value) { | ||
| 74 | unsigned long trailing_zero = 0; | ||
| 75 | |||
| 76 | if (_BitScanForward64(&trailing_zero, value) != 0) { | ||
| 77 | return trailing_zero; | ||
| 78 | } | ||
| 79 | |||
| 80 | return 64; | ||
| 81 | } | ||
| 82 | #else | ||
| 83 | inline u32 CountTrailingZeroes32(u32 value) { | ||
| 84 | if (value == 0) { | ||
| 85 | return 32; | ||
| 86 | } | ||
| 87 | |||
| 88 | return __builtin_ctz(value); | ||
| 89 | } | ||
| 90 | |||
| 91 | inline u64 CountTrailingZeroes64(u64 value) { | ||
| 92 | if (value == 0) { | ||
| 93 | return 64; | ||
| 94 | } | ||
| 95 | |||
| 96 | return __builtin_ctzll(value); | ||
| 97 | } | ||
| 98 | #endif | ||
| 99 | |||
| 61 | } // namespace Common | 100 | } // namespace Common |
diff --git a/src/common/multi_level_queue.h b/src/common/multi_level_queue.h new file mode 100644 index 000000000..2b61b91e0 --- /dev/null +++ b/src/common/multi_level_queue.h | |||
| @@ -0,0 +1,337 @@ | |||
| 1 | // Copyright 2019 TuxSH | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <iterator> | ||
| 9 | #include <list> | ||
| 10 | #include <utility> | ||
| 11 | |||
| 12 | #include "common/bit_util.h" | ||
| 13 | #include "common/common_types.h" | ||
| 14 | |||
| 15 | namespace Common { | ||
| 16 | |||
| 17 | /** | ||
| 18 | * A MultiLevelQueue is a type of priority queue which has the following characteristics: | ||
| 19 | * - iteratable through each of its elements. | ||
| 20 | * - back can be obtained. | ||
| 21 | * - O(1) add, lookup (both front and back) | ||
| 22 | * - discrete priorities and a max of 64 priorities (limited domain) | ||
| 23 | * This type of priority queue is normaly used for managing threads within an scheduler | ||
| 24 | */ | ||
| 25 | template <typename T, std::size_t Depth> | ||
| 26 | class MultiLevelQueue { | ||
| 27 | public: | ||
| 28 | using value_type = T; | ||
| 29 | using reference = value_type&; | ||
| 30 | using const_reference = const value_type&; | ||
| 31 | using pointer = value_type*; | ||
| 32 | using const_pointer = const value_type*; | ||
| 33 | |||
| 34 | using difference_type = typename std::pointer_traits<pointer>::difference_type; | ||
| 35 | using size_type = std::size_t; | ||
| 36 | |||
| 37 | template <bool is_constant> | ||
| 38 | class iterator_impl { | ||
| 39 | public: | ||
| 40 | using iterator_category = std::bidirectional_iterator_tag; | ||
| 41 | using value_type = T; | ||
| 42 | using pointer = std::conditional_t<is_constant, T*, const T*>; | ||
| 43 | using reference = std::conditional_t<is_constant, const T&, T&>; | ||
| 44 | using difference_type = typename std::pointer_traits<pointer>::difference_type; | ||
| 45 | |||
| 46 | friend bool operator==(const iterator_impl& lhs, const iterator_impl& rhs) { | ||
| 47 | if (lhs.IsEnd() && rhs.IsEnd()) | ||
| 48 | return true; | ||
| 49 | return std::tie(lhs.current_priority, lhs.it) == std::tie(rhs.current_priority, rhs.it); | ||
| 50 | } | ||
| 51 | |||
| 52 | friend bool operator!=(const iterator_impl& lhs, const iterator_impl& rhs) { | ||
| 53 | return !operator==(lhs, rhs); | ||
| 54 | } | ||
| 55 | |||
| 56 | reference operator*() const { | ||
| 57 | return *it; | ||
| 58 | } | ||
| 59 | |||
| 60 | pointer operator->() const { | ||
| 61 | return it.operator->(); | ||
| 62 | } | ||
| 63 | |||
| 64 | iterator_impl& operator++() { | ||
| 65 | if (IsEnd()) { | ||
| 66 | return *this; | ||
| 67 | } | ||
| 68 | |||
| 69 | ++it; | ||
| 70 | |||
| 71 | if (it == GetEndItForPrio()) { | ||
| 72 | u64 prios = mlq.used_priorities; | ||
| 73 | prios &= ~((1ULL << (current_priority + 1)) - 1); | ||
| 74 | if (prios == 0) { | ||
| 75 | current_priority = mlq.depth(); | ||
| 76 | } else { | ||
| 77 | current_priority = CountTrailingZeroes64(prios); | ||
| 78 | it = GetBeginItForPrio(); | ||
| 79 | } | ||
| 80 | } | ||
| 81 | return *this; | ||
| 82 | } | ||
| 83 | |||
| 84 | iterator_impl& operator--() { | ||
| 85 | if (IsEnd()) { | ||
| 86 | if (mlq.used_priorities != 0) { | ||
| 87 | current_priority = 63 - CountLeadingZeroes64(mlq.used_priorities); | ||
| 88 | it = GetEndItForPrio(); | ||
| 89 | --it; | ||
| 90 | } | ||
| 91 | } else if (it == GetBeginItForPrio()) { | ||
| 92 | u64 prios = mlq.used_priorities; | ||
| 93 | prios &= (1ULL << current_priority) - 1; | ||
| 94 | if (prios != 0) { | ||
| 95 | current_priority = CountTrailingZeroes64(prios); | ||
| 96 | it = GetEndItForPrio(); | ||
| 97 | --it; | ||
| 98 | } | ||
| 99 | } else { | ||
| 100 | --it; | ||
| 101 | } | ||
| 102 | return *this; | ||
| 103 | } | ||
| 104 | |||
| 105 | iterator_impl operator++(int) { | ||
| 106 | const iterator_impl v{*this}; | ||
| 107 | ++(*this); | ||
| 108 | return v; | ||
| 109 | } | ||
| 110 | |||
| 111 | iterator_impl operator--(int) { | ||
| 112 | const iterator_impl v{*this}; | ||
| 113 | --(*this); | ||
| 114 | return v; | ||
| 115 | } | ||
| 116 | |||
| 117 | // allow implicit const->non-const | ||
| 118 | iterator_impl(const iterator_impl<false>& other) | ||
| 119 | : mlq(other.mlq), it(other.it), current_priority(other.current_priority) {} | ||
| 120 | |||
| 121 | iterator_impl(const iterator_impl<true>& other) | ||
| 122 | : mlq(other.mlq), it(other.it), current_priority(other.current_priority) {} | ||
| 123 | |||
| 124 | iterator_impl& operator=(const iterator_impl<false>& other) { | ||
| 125 | mlq = other.mlq; | ||
| 126 | it = other.it; | ||
| 127 | current_priority = other.current_priority; | ||
| 128 | return *this; | ||
| 129 | } | ||
| 130 | |||
| 131 | friend class iterator_impl<true>; | ||
| 132 | iterator_impl() = default; | ||
| 133 | |||
| 134 | private: | ||
| 135 | friend class MultiLevelQueue; | ||
| 136 | using container_ref = | ||
| 137 | std::conditional_t<is_constant, const MultiLevelQueue&, MultiLevelQueue&>; | ||
| 138 | using list_iterator = std::conditional_t<is_constant, typename std::list<T>::const_iterator, | ||
| 139 | typename std::list<T>::iterator>; | ||
| 140 | |||
| 141 | explicit iterator_impl(container_ref mlq, list_iterator it, u32 current_priority) | ||
| 142 | : mlq(mlq), it(it), current_priority(current_priority) {} | ||
| 143 | explicit iterator_impl(container_ref mlq, u32 current_priority) | ||
| 144 | : mlq(mlq), it(), current_priority(current_priority) {} | ||
| 145 | |||
| 146 | bool IsEnd() const { | ||
| 147 | return current_priority == mlq.depth(); | ||
| 148 | } | ||
| 149 | |||
| 150 | list_iterator GetBeginItForPrio() const { | ||
| 151 | return mlq.levels[current_priority].begin(); | ||
| 152 | } | ||
| 153 | |||
| 154 | list_iterator GetEndItForPrio() const { | ||
| 155 | return mlq.levels[current_priority].end(); | ||
| 156 | } | ||
| 157 | |||
| 158 | container_ref mlq; | ||
| 159 | list_iterator it; | ||
| 160 | u32 current_priority; | ||
| 161 | }; | ||
| 162 | |||
| 163 | using iterator = iterator_impl<false>; | ||
| 164 | using const_iterator = iterator_impl<true>; | ||
| 165 | |||
| 166 | void add(const T& element, u32 priority, bool send_back = true) { | ||
| 167 | if (send_back) | ||
| 168 | levels[priority].push_back(element); | ||
| 169 | else | ||
| 170 | levels[priority].push_front(element); | ||
| 171 | used_priorities |= 1ULL << priority; | ||
| 172 | } | ||
| 173 | |||
| 174 | void remove(const T& element, u32 priority) { | ||
| 175 | auto it = ListIterateTo(levels[priority], element); | ||
| 176 | if (it == levels[priority].end()) | ||
| 177 | return; | ||
| 178 | levels[priority].erase(it); | ||
| 179 | if (levels[priority].empty()) { | ||
| 180 | used_priorities &= ~(1ULL << priority); | ||
| 181 | } | ||
| 182 | } | ||
| 183 | |||
| 184 | void adjust(const T& element, u32 old_priority, u32 new_priority, bool adjust_front = false) { | ||
| 185 | remove(element, old_priority); | ||
| 186 | add(element, new_priority, !adjust_front); | ||
| 187 | } | ||
| 188 | void adjust(const_iterator it, u32 old_priority, u32 new_priority, bool adjust_front = false) { | ||
| 189 | adjust(*it, old_priority, new_priority, adjust_front); | ||
| 190 | } | ||
| 191 | |||
| 192 | void transfer_to_front(const T& element, u32 priority, MultiLevelQueue& other) { | ||
| 193 | ListSplice(other.levels[priority], other.levels[priority].begin(), levels[priority], | ||
| 194 | ListIterateTo(levels[priority], element)); | ||
| 195 | |||
| 196 | other.used_priorities |= 1ULL << priority; | ||
| 197 | |||
| 198 | if (levels[priority].empty()) { | ||
| 199 | used_priorities &= ~(1ULL << priority); | ||
| 200 | } | ||
| 201 | } | ||
| 202 | |||
| 203 | void transfer_to_front(const_iterator it, u32 priority, MultiLevelQueue& other) { | ||
| 204 | transfer_to_front(*it, priority, other); | ||
| 205 | } | ||
| 206 | |||
| 207 | void transfer_to_back(const T& element, u32 priority, MultiLevelQueue& other) { | ||
| 208 | ListSplice(other.levels[priority], other.levels[priority].end(), levels[priority], | ||
| 209 | ListIterateTo(levels[priority], element)); | ||
| 210 | |||
| 211 | other.used_priorities |= 1ULL << priority; | ||
| 212 | |||
| 213 | if (levels[priority].empty()) { | ||
| 214 | used_priorities &= ~(1ULL << priority); | ||
| 215 | } | ||
| 216 | } | ||
| 217 | |||
| 218 | void transfer_to_back(const_iterator it, u32 priority, MultiLevelQueue& other) { | ||
| 219 | transfer_to_back(*it, priority, other); | ||
| 220 | } | ||
| 221 | |||
| 222 | void yield(u32 priority, std::size_t n = 1) { | ||
| 223 | ListShiftForward(levels[priority], n); | ||
| 224 | } | ||
| 225 | |||
| 226 | std::size_t depth() const { | ||
| 227 | return Depth; | ||
| 228 | } | ||
| 229 | |||
| 230 | std::size_t size(u32 priority) const { | ||
| 231 | return levels[priority].size(); | ||
| 232 | } | ||
| 233 | |||
| 234 | std::size_t size() const { | ||
| 235 | u64 priorities = used_priorities; | ||
| 236 | std::size_t size = 0; | ||
| 237 | while (priorities != 0) { | ||
| 238 | const u64 current_priority = CountTrailingZeroes64(priorities); | ||
| 239 | size += levels[current_priority].size(); | ||
| 240 | priorities &= ~(1ULL << current_priority); | ||
| 241 | } | ||
| 242 | return size; | ||
| 243 | } | ||
| 244 | |||
| 245 | bool empty() const { | ||
| 246 | return used_priorities == 0; | ||
| 247 | } | ||
| 248 | |||
| 249 | bool empty(u32 priority) const { | ||
| 250 | return (used_priorities & (1ULL << priority)) == 0; | ||
| 251 | } | ||
| 252 | |||
| 253 | u32 highest_priority_set(u32 max_priority = 0) const { | ||
| 254 | const u64 priorities = | ||
| 255 | max_priority == 0 ? used_priorities : (used_priorities & ~((1ULL << max_priority) - 1)); | ||
| 256 | return priorities == 0 ? Depth : static_cast<u32>(CountTrailingZeroes64(priorities)); | ||
| 257 | } | ||
| 258 | |||
| 259 | u32 lowest_priority_set(u32 min_priority = Depth - 1) const { | ||
| 260 | const u64 priorities = min_priority >= Depth - 1 | ||
| 261 | ? used_priorities | ||
| 262 | : (used_priorities & ((1ULL << (min_priority + 1)) - 1)); | ||
| 263 | return priorities == 0 ? Depth : 63 - CountLeadingZeroes64(priorities); | ||
| 264 | } | ||
| 265 | |||
| 266 | const_iterator cbegin(u32 max_prio = 0) const { | ||
| 267 | const u32 priority = highest_priority_set(max_prio); | ||
| 268 | return priority == Depth ? cend() | ||
| 269 | : const_iterator{*this, levels[priority].cbegin(), priority}; | ||
| 270 | } | ||
| 271 | const_iterator begin(u32 max_prio = 0) const { | ||
| 272 | return cbegin(max_prio); | ||
| 273 | } | ||
| 274 | iterator begin(u32 max_prio = 0) { | ||
| 275 | const u32 priority = highest_priority_set(max_prio); | ||
| 276 | return priority == Depth ? end() : iterator{*this, levels[priority].begin(), priority}; | ||
| 277 | } | ||
| 278 | |||
| 279 | const_iterator cend(u32 min_prio = Depth - 1) const { | ||
| 280 | return min_prio == Depth - 1 ? const_iterator{*this, Depth} : cbegin(min_prio + 1); | ||
| 281 | } | ||
| 282 | const_iterator end(u32 min_prio = Depth - 1) const { | ||
| 283 | return cend(min_prio); | ||
| 284 | } | ||
| 285 | iterator end(u32 min_prio = Depth - 1) { | ||
| 286 | return min_prio == Depth - 1 ? iterator{*this, Depth} : begin(min_prio + 1); | ||
| 287 | } | ||
| 288 | |||
| 289 | T& front(u32 max_priority = 0) { | ||
| 290 | const u32 priority = highest_priority_set(max_priority); | ||
| 291 | return levels[priority == Depth ? 0 : priority].front(); | ||
| 292 | } | ||
| 293 | const T& front(u32 max_priority = 0) const { | ||
| 294 | const u32 priority = highest_priority_set(max_priority); | ||
| 295 | return levels[priority == Depth ? 0 : priority].front(); | ||
| 296 | } | ||
| 297 | |||
| 298 | T back(u32 min_priority = Depth - 1) { | ||
| 299 | const u32 priority = lowest_priority_set(min_priority); // intended | ||
| 300 | return levels[priority == Depth ? 63 : priority].back(); | ||
| 301 | } | ||
| 302 | const T& back(u32 min_priority = Depth - 1) const { | ||
| 303 | const u32 priority = lowest_priority_set(min_priority); // intended | ||
| 304 | return levels[priority == Depth ? 63 : priority].back(); | ||
| 305 | } | ||
| 306 | |||
| 307 | private: | ||
| 308 | using const_list_iterator = typename std::list<T>::const_iterator; | ||
| 309 | |||
| 310 | static void ListShiftForward(std::list<T>& list, const std::size_t shift = 1) { | ||
| 311 | if (shift >= list.size()) { | ||
| 312 | return; | ||
| 313 | } | ||
| 314 | |||
| 315 | const auto begin_range = list.begin(); | ||
| 316 | const auto end_range = std::next(begin_range, shift); | ||
| 317 | list.splice(list.end(), list, begin_range, end_range); | ||
| 318 | } | ||
| 319 | |||
| 320 | static void ListSplice(std::list<T>& in_list, const_list_iterator position, | ||
| 321 | std::list<T>& out_list, const_list_iterator element) { | ||
| 322 | in_list.splice(position, out_list, element); | ||
| 323 | } | ||
| 324 | |||
| 325 | static const_list_iterator ListIterateTo(const std::list<T>& list, const T& element) { | ||
| 326 | auto it = list.cbegin(); | ||
| 327 | while (it != list.cend() && *it != element) { | ||
| 328 | ++it; | ||
| 329 | } | ||
| 330 | return it; | ||
| 331 | } | ||
| 332 | |||
| 333 | std::array<std::list<T>, Depth> levels; | ||
| 334 | u64 used_priorities = 0; | ||
| 335 | }; | ||
| 336 | |||
| 337 | } // namespace Common | ||
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index cc189cc64..6d0f13ecf 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp | |||
| @@ -30,7 +30,7 @@ Scheduler::~Scheduler() { | |||
| 30 | 30 | ||
| 31 | bool Scheduler::HaveReadyThreads() const { | 31 | bool Scheduler::HaveReadyThreads() const { |
| 32 | std::lock_guard<std::mutex> lock(scheduler_mutex); | 32 | std::lock_guard<std::mutex> lock(scheduler_mutex); |
| 33 | return ready_queue.get_first() != nullptr; | 33 | return !ready_queue.empty(); |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | Thread* Scheduler::GetCurrentThread() const { | 36 | Thread* Scheduler::GetCurrentThread() const { |
| @@ -46,22 +46,27 @@ Thread* Scheduler::PopNextReadyThread() { | |||
| 46 | Thread* thread = GetCurrentThread(); | 46 | Thread* thread = GetCurrentThread(); |
| 47 | 47 | ||
| 48 | if (thread && thread->GetStatus() == ThreadStatus::Running) { | 48 | if (thread && thread->GetStatus() == ThreadStatus::Running) { |
| 49 | if (ready_queue.empty()) { | ||
| 50 | return thread; | ||
| 51 | } | ||
| 49 | // We have to do better than the current thread. | 52 | // We have to do better than the current thread. |
| 50 | // This call returns null when that's not possible. | 53 | // This call returns null when that's not possible. |
| 51 | next = ready_queue.pop_first_better(thread->GetPriority()); | 54 | next = ready_queue.front(); |
| 52 | if (!next) { | 55 | if (next == nullptr || next->GetPriority() >= thread->GetPriority()) { |
| 53 | // Otherwise just keep going with the current thread | ||
| 54 | next = thread; | 56 | next = thread; |
| 55 | } | 57 | } |
| 56 | } else { | 58 | } else { |
| 57 | next = ready_queue.pop_first(); | 59 | if (ready_queue.empty()) { |
| 60 | return nullptr; | ||
| 61 | } | ||
| 62 | next = ready_queue.front(); | ||
| 58 | } | 63 | } |
| 59 | 64 | ||
| 60 | return next; | 65 | return next; |
| 61 | } | 66 | } |
| 62 | 67 | ||
| 63 | void Scheduler::SwitchContext(Thread* new_thread) { | 68 | void Scheduler::SwitchContext(Thread* new_thread) { |
| 64 | Thread* const previous_thread = GetCurrentThread(); | 69 | Thread* previous_thread = GetCurrentThread(); |
| 65 | Process* const previous_process = system.Kernel().CurrentProcess(); | 70 | Process* const previous_process = system.Kernel().CurrentProcess(); |
| 66 | 71 | ||
| 67 | UpdateLastContextSwitchTime(previous_thread, previous_process); | 72 | UpdateLastContextSwitchTime(previous_thread, previous_process); |
| @@ -75,7 +80,7 @@ void Scheduler::SwitchContext(Thread* new_thread) { | |||
| 75 | if (previous_thread->GetStatus() == ThreadStatus::Running) { | 80 | if (previous_thread->GetStatus() == ThreadStatus::Running) { |
| 76 | // This is only the case when a reschedule is triggered without the current thread | 81 | // This is only the case when a reschedule is triggered without the current thread |
| 77 | // yielding execution (i.e. an event triggered, system core time-sliced, etc) | 82 | // yielding execution (i.e. an event triggered, system core time-sliced, etc) |
| 78 | ready_queue.push_front(previous_thread->GetPriority(), previous_thread); | 83 | ready_queue.add(previous_thread, previous_thread->GetPriority(), false); |
| 79 | previous_thread->SetStatus(ThreadStatus::Ready); | 84 | previous_thread->SetStatus(ThreadStatus::Ready); |
| 80 | } | 85 | } |
| 81 | } | 86 | } |
| @@ -90,7 +95,7 @@ void Scheduler::SwitchContext(Thread* new_thread) { | |||
| 90 | 95 | ||
| 91 | current_thread = new_thread; | 96 | current_thread = new_thread; |
| 92 | 97 | ||
| 93 | ready_queue.remove(new_thread->GetPriority(), new_thread); | 98 | ready_queue.remove(new_thread, new_thread->GetPriority()); |
| 94 | new_thread->SetStatus(ThreadStatus::Running); | 99 | new_thread->SetStatus(ThreadStatus::Running); |
| 95 | 100 | ||
| 96 | auto* const thread_owner_process = current_thread->GetOwnerProcess(); | 101 | auto* const thread_owner_process = current_thread->GetOwnerProcess(); |
| @@ -147,7 +152,6 @@ void Scheduler::AddThread(SharedPtr<Thread> thread, u32 priority) { | |||
| 147 | std::lock_guard<std::mutex> lock(scheduler_mutex); | 152 | std::lock_guard<std::mutex> lock(scheduler_mutex); |
| 148 | 153 | ||
| 149 | thread_list.push_back(std::move(thread)); | 154 | thread_list.push_back(std::move(thread)); |
| 150 | ready_queue.prepare(priority); | ||
| 151 | } | 155 | } |
| 152 | 156 | ||
| 153 | void Scheduler::RemoveThread(Thread* thread) { | 157 | void Scheduler::RemoveThread(Thread* thread) { |
| @@ -161,33 +165,37 @@ void Scheduler::ScheduleThread(Thread* thread, u32 priority) { | |||
| 161 | std::lock_guard<std::mutex> lock(scheduler_mutex); | 165 | std::lock_guard<std::mutex> lock(scheduler_mutex); |
| 162 | 166 | ||
| 163 | ASSERT(thread->GetStatus() == ThreadStatus::Ready); | 167 | ASSERT(thread->GetStatus() == ThreadStatus::Ready); |
| 164 | ready_queue.push_back(priority, thread); | 168 | ready_queue.add(thread, priority); |
| 165 | } | 169 | } |
| 166 | 170 | ||
| 167 | void Scheduler::UnscheduleThread(Thread* thread, u32 priority) { | 171 | void Scheduler::UnscheduleThread(Thread* thread, u32 priority) { |
| 168 | std::lock_guard<std::mutex> lock(scheduler_mutex); | 172 | std::lock_guard<std::mutex> lock(scheduler_mutex); |
| 169 | 173 | ||
| 170 | ASSERT(thread->GetStatus() == ThreadStatus::Ready); | 174 | ASSERT(thread->GetStatus() == ThreadStatus::Ready); |
| 171 | ready_queue.remove(priority, thread); | 175 | ready_queue.remove(thread, priority); |
| 172 | } | 176 | } |
| 173 | 177 | ||
| 174 | void Scheduler::SetThreadPriority(Thread* thread, u32 priority) { | 178 | void Scheduler::SetThreadPriority(Thread* thread, u32 priority) { |
| 175 | std::lock_guard<std::mutex> lock(scheduler_mutex); | 179 | std::lock_guard<std::mutex> lock(scheduler_mutex); |
| 180 | if (thread->GetPriority() == priority) { | ||
| 181 | return; | ||
| 182 | } | ||
| 176 | 183 | ||
| 177 | // If thread was ready, adjust queues | 184 | // If thread was ready, adjust queues |
| 178 | if (thread->GetStatus() == ThreadStatus::Ready) | 185 | if (thread->GetStatus() == ThreadStatus::Ready) |
| 179 | ready_queue.move(thread, thread->GetPriority(), priority); | 186 | ready_queue.adjust(thread, thread->GetPriority(), priority); |
| 180 | else | ||
| 181 | ready_queue.prepare(priority); | ||
| 182 | } | 187 | } |
| 183 | 188 | ||
| 184 | Thread* Scheduler::GetNextSuggestedThread(u32 core, u32 maximum_priority) const { | 189 | Thread* Scheduler::GetNextSuggestedThread(u32 core, u32 maximum_priority) const { |
| 185 | std::lock_guard<std::mutex> lock(scheduler_mutex); | 190 | std::lock_guard<std::mutex> lock(scheduler_mutex); |
| 186 | 191 | ||
| 187 | const u32 mask = 1U << core; | 192 | const u32 mask = 1U << core; |
| 188 | return ready_queue.get_first_filter([mask, maximum_priority](Thread const* thread) { | 193 | for (auto* thread : ready_queue) { |
| 189 | return (thread->GetAffinityMask() & mask) != 0 && thread->GetPriority() < maximum_priority; | 194 | if ((thread->GetAffinityMask() & mask) != 0 && thread->GetPriority() < maximum_priority) { |
| 190 | }); | 195 | return thread; |
| 196 | } | ||
| 197 | } | ||
| 198 | return nullptr; | ||
| 191 | } | 199 | } |
| 192 | 200 | ||
| 193 | void Scheduler::YieldWithoutLoadBalancing(Thread* thread) { | 201 | void Scheduler::YieldWithoutLoadBalancing(Thread* thread) { |
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index 1c5bf57d9..44baeb713 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | #include <mutex> | 7 | #include <mutex> |
| 8 | #include <vector> | 8 | #include <vector> |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "common/thread_queue_list.h" | 10 | #include "common/multi_level_queue.h" |
| 11 | #include "core/hle/kernel/object.h" | 11 | #include "core/hle/kernel/object.h" |
| 12 | #include "core/hle/kernel/thread.h" | 12 | #include "core/hle/kernel/thread.h" |
| 13 | 13 | ||
| @@ -156,7 +156,7 @@ private: | |||
| 156 | std::vector<SharedPtr<Thread>> thread_list; | 156 | std::vector<SharedPtr<Thread>> thread_list; |
| 157 | 157 | ||
| 158 | /// Lists only ready thread ids. | 158 | /// Lists only ready thread ids. |
| 159 | Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST + 1> ready_queue; | 159 | Common::MultiLevelQueue<Thread*, THREADPRIO_LOWEST + 1> ready_queue; |
| 160 | 160 | ||
| 161 | SharedPtr<Thread> current_thread = nullptr; | 161 | SharedPtr<Thread> current_thread = nullptr; |
| 162 | 162 | ||
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index d0284bdf4..c7038b217 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt | |||
| @@ -1,5 +1,7 @@ | |||
| 1 | add_executable(tests | 1 | add_executable(tests |
| 2 | common/bit_field.cpp | 2 | common/bit_field.cpp |
| 3 | common/bit_utils.cpp | ||
| 4 | common/multi_level_queue.cpp | ||
| 3 | common/param_package.cpp | 5 | common/param_package.cpp |
| 4 | common/ring_buffer.cpp | 6 | common/ring_buffer.cpp |
| 5 | core/arm/arm_test_common.cpp | 7 | core/arm/arm_test_common.cpp |
diff --git a/src/tests/common/bit_utils.cpp b/src/tests/common/bit_utils.cpp new file mode 100644 index 000000000..479b5995a --- /dev/null +++ b/src/tests/common/bit_utils.cpp | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | // Copyright 2017 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <catch2/catch.hpp> | ||
| 6 | #include <math.h> | ||
| 7 | #include "common/bit_util.h" | ||
| 8 | |||
| 9 | namespace Common { | ||
| 10 | |||
| 11 | TEST_CASE("BitUtils::CountTrailingZeroes", "[common]") { | ||
| 12 | REQUIRE(Common::CountTrailingZeroes32(0) == 32); | ||
| 13 | REQUIRE(Common::CountTrailingZeroes64(0) == 64); | ||
| 14 | REQUIRE(Common::CountTrailingZeroes32(9) == 0); | ||
| 15 | REQUIRE(Common::CountTrailingZeroes32(8) == 3); | ||
| 16 | REQUIRE(Common::CountTrailingZeroes32(0x801000) == 12); | ||
| 17 | REQUIRE(Common::CountTrailingZeroes64(9) == 0); | ||
| 18 | REQUIRE(Common::CountTrailingZeroes64(8) == 3); | ||
| 19 | REQUIRE(Common::CountTrailingZeroes64(0x801000) == 12); | ||
| 20 | REQUIRE(Common::CountTrailingZeroes64(0x801000000000UL) == 36); | ||
| 21 | } | ||
| 22 | |||
| 23 | } // namespace Common | ||
diff --git a/src/tests/common/multi_level_queue.cpp b/src/tests/common/multi_level_queue.cpp new file mode 100644 index 000000000..cca7ec7da --- /dev/null +++ b/src/tests/common/multi_level_queue.cpp | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | // Copyright 2019 Yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <catch2/catch.hpp> | ||
| 6 | #include <math.h> | ||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/multi_level_queue.h" | ||
| 9 | |||
| 10 | namespace Common { | ||
| 11 | |||
| 12 | TEST_CASE("MultiLevelQueue", "[common]") { | ||
| 13 | std::array<f32, 8> values = {0.0, 5.0, 1.0, 9.0, 8.0, 2.0, 6.0, 7.0}; | ||
| 14 | Common::MultiLevelQueue<f32, 64> mlq; | ||
| 15 | REQUIRE(mlq.empty()); | ||
| 16 | mlq.add(values[2], 2); | ||
| 17 | mlq.add(values[7], 7); | ||
| 18 | mlq.add(values[3], 3); | ||
| 19 | mlq.add(values[4], 4); | ||
| 20 | mlq.add(values[0], 0); | ||
| 21 | mlq.add(values[5], 5); | ||
| 22 | mlq.add(values[6], 6); | ||
| 23 | mlq.add(values[1], 1); | ||
| 24 | u32 index = 0; | ||
| 25 | bool all_set = true; | ||
| 26 | for (auto& f : mlq) { | ||
| 27 | all_set &= (f == values[index]); | ||
| 28 | index++; | ||
| 29 | } | ||
| 30 | REQUIRE(all_set); | ||
| 31 | REQUIRE(!mlq.empty()); | ||
| 32 | f32 v = 8.0; | ||
| 33 | mlq.add(v, 2); | ||
| 34 | v = -7.0; | ||
| 35 | mlq.add(v, 2, false); | ||
| 36 | REQUIRE(mlq.front(2) == -7.0); | ||
| 37 | mlq.yield(2); | ||
| 38 | REQUIRE(mlq.front(2) == values[2]); | ||
| 39 | REQUIRE(mlq.back(2) == -7.0); | ||
| 40 | REQUIRE(mlq.empty(8)); | ||
| 41 | v = 10.0; | ||
| 42 | mlq.add(v, 8); | ||
| 43 | mlq.adjust(v, 8, 9); | ||
| 44 | REQUIRE(mlq.front(9) == v); | ||
| 45 | REQUIRE(mlq.empty(8)); | ||
| 46 | REQUIRE(!mlq.empty(9)); | ||
| 47 | mlq.adjust(values[0], 0, 9); | ||
| 48 | REQUIRE(mlq.highest_priority_set() == 1); | ||
| 49 | REQUIRE(mlq.lowest_priority_set() == 9); | ||
| 50 | mlq.remove(values[1], 1); | ||
| 51 | REQUIRE(mlq.highest_priority_set() == 2); | ||
| 52 | REQUIRE(mlq.empty(1)); | ||
| 53 | } | ||
| 54 | |||
| 55 | } // namespace Common | ||