mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu-mainline.git
				synced 2025-03-21 01:53:15 +00:00 
			
		
		
		
	 f71c598907
			
		
	
	
		f71c598907
		
	
	
	
	
		
			
			We really don't need to pull in several headers of boost related machinery just to perform the erase-remove idiom (particularly with C++20 around the corner, which adds universal container std::erase and std::erase_if, which we can just use instead). With this, we don't need to link in anything boost-related into common.
		
			
				
	
	
		
			178 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			178 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Copyright 2014 Citra Emulator Project / PPSSPP Project
 | |
| // Licensed under GPLv2 or any later version
 | |
| // Refer to the license.txt file included.
 | |
| 
 | |
| #pragma once
 | |
| 
 | |
| #include <array>
 | |
| #include <deque>
 | |
| 
 | |
| namespace Common {
 | |
| 
 | |
| template <class T, unsigned int N>
 | |
| struct ThreadQueueList {
 | |
|     // TODO(yuriks): If performance proves to be a problem, the std::deques can be replaced with
 | |
|     //               (dynamically resizable) circular buffers to remove their overhead when
 | |
|     //               inserting and popping.
 | |
| 
 | |
|     using Priority = unsigned int;
 | |
| 
 | |
|     // Number of priority levels. (Valid levels are [0..NUM_QUEUES).)
 | |
|     static const Priority NUM_QUEUES = N;
 | |
| 
 | |
|     ThreadQueueList() {
 | |
|         first = nullptr;
 | |
|     }
 | |
| 
 | |
|     // Only for debugging, returns priority level.
 | |
|     Priority contains(const T& uid) const {
 | |
|         for (Priority i = 0; i < NUM_QUEUES; ++i) {
 | |
|             const Queue& cur = queues[i];
 | |
|             if (std::find(cur.data.cbegin(), cur.data.cend(), uid) != cur.data.cend()) {
 | |
|                 return i;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     T get_first() const {
 | |
|         const Queue* cur = first;
 | |
|         while (cur != nullptr) {
 | |
|             if (!cur->data.empty()) {
 | |
|                 return cur->data.front();
 | |
|             }
 | |
|             cur = cur->next_nonempty;
 | |
|         }
 | |
| 
 | |
|         return T();
 | |
|     }
 | |
| 
 | |
|     template <typename UnaryPredicate>
 | |
|     T get_first_filter(UnaryPredicate filter) const {
 | |
|         const Queue* cur = first;
 | |
|         while (cur != nullptr) {
 | |
|             if (!cur->data.empty()) {
 | |
|                 for (const auto& item : cur->data) {
 | |
|                     if (filter(item))
 | |
|                         return item;
 | |
|                 }
 | |
|             }
 | |
|             cur = cur->next_nonempty;
 | |
|         }
 | |
| 
 | |
|         return T();
 | |
|     }
 | |
| 
 | |
|     T pop_first() {
 | |
|         Queue* cur = first;
 | |
|         while (cur != nullptr) {
 | |
|             if (!cur->data.empty()) {
 | |
|                 auto tmp = std::move(cur->data.front());
 | |
|                 cur->data.pop_front();
 | |
|                 return tmp;
 | |
|             }
 | |
|             cur = cur->next_nonempty;
 | |
|         }
 | |
| 
 | |
|         return T();
 | |
|     }
 | |
| 
 | |
|     T pop_first_better(Priority priority) {
 | |
|         Queue* cur = first;
 | |
|         Queue* stop = &queues[priority];
 | |
|         while (cur < stop) {
 | |
|             if (!cur->data.empty()) {
 | |
|                 auto tmp = std::move(cur->data.front());
 | |
|                 cur->data.pop_front();
 | |
|                 return tmp;
 | |
|             }
 | |
|             cur = cur->next_nonempty;
 | |
|         }
 | |
| 
 | |
|         return T();
 | |
|     }
 | |
| 
 | |
|     void push_front(Priority priority, const T& thread_id) {
 | |
|         Queue* cur = &queues[priority];
 | |
|         cur->data.push_front(thread_id);
 | |
|     }
 | |
| 
 | |
|     void push_back(Priority priority, const T& thread_id) {
 | |
|         Queue* cur = &queues[priority];
 | |
|         cur->data.push_back(thread_id);
 | |
|     }
 | |
| 
 | |
|     void move(const T& thread_id, Priority old_priority, Priority new_priority) {
 | |
|         remove(old_priority, thread_id);
 | |
|         prepare(new_priority);
 | |
|         push_back(new_priority, thread_id);
 | |
|     }
 | |
| 
 | |
|     void remove(Priority priority, const T& thread_id) {
 | |
|         Queue* const cur = &queues[priority];
 | |
|         const auto iter = std::remove(cur->data.begin(), cur->data.end(), thread_id);
 | |
|         cur->data.erase(iter, cur->data.end());
 | |
|     }
 | |
| 
 | |
|     void rotate(Priority priority) {
 | |
|         Queue* cur = &queues[priority];
 | |
| 
 | |
|         if (cur->data.size() > 1) {
 | |
|             cur->data.push_back(std::move(cur->data.front()));
 | |
|             cur->data.pop_front();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void clear() {
 | |
|         queues.fill(Queue());
 | |
|         first = nullptr;
 | |
|     }
 | |
| 
 | |
|     bool empty(Priority priority) const {
 | |
|         const Queue* cur = &queues[priority];
 | |
|         return cur->data.empty();
 | |
|     }
 | |
| 
 | |
|     void prepare(Priority priority) {
 | |
|         Queue* cur = &queues[priority];
 | |
|         if (cur->next_nonempty == UnlinkedTag())
 | |
|             link(priority);
 | |
|     }
 | |
| 
 | |
| private:
 | |
|     struct Queue {
 | |
|         // Points to the next active priority, skipping over ones that have never been used.
 | |
|         Queue* next_nonempty = UnlinkedTag();
 | |
|         // Double-ended queue of threads in this priority level
 | |
|         std::deque<T> data;
 | |
|     };
 | |
| 
 | |
|     /// Special tag used to mark priority levels that have never been used.
 | |
|     static Queue* UnlinkedTag() {
 | |
|         return reinterpret_cast<Queue*>(1);
 | |
|     }
 | |
| 
 | |
|     void link(Priority priority) {
 | |
|         Queue* cur = &queues[priority];
 | |
| 
 | |
|         for (int i = priority - 1; i >= 0; --i) {
 | |
|             if (queues[i].next_nonempty != UnlinkedTag()) {
 | |
|                 cur->next_nonempty = queues[i].next_nonempty;
 | |
|                 queues[i].next_nonempty = cur;
 | |
|                 return;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         cur->next_nonempty = first;
 | |
|         first = cur;
 | |
|     }
 | |
| 
 | |
|     // The first queue that's ever been used.
 | |
|     Queue* first;
 | |
|     // The priority level queues of thread ids.
 | |
|     std::array<Queue, NUM_QUEUES> queues;
 | |
| };
 | |
| 
 | |
| } // namespace Common
 |