mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	Kernel/Mutex: Propagate thread priority changes to other threads inheriting the priority via mutexes
This commit is contained in:
		
							parent
							
								
									b6a0355568
								
							
						
					
					
						commit
						d3ff5b91e1
					
				
					 5 changed files with 60 additions and 42 deletions
				
			
		|  | @ -13,38 +13,6 @@ | ||||||
| 
 | 
 | ||||||
| namespace Kernel { | namespace Kernel { | ||||||
| 
 | 
 | ||||||
| /**
 |  | ||||||
|  * Boost's a thread's priority to the best priority among the thread's held mutexes. |  | ||||||
|  * This prevents priority inversion via priority inheritance. |  | ||||||
|  */ |  | ||||||
| static void UpdateThreadPriority(Thread* thread) { |  | ||||||
|     s32 best_priority = THREADPRIO_LOWEST; |  | ||||||
|     for (auto& mutex : thread->held_mutexes) { |  | ||||||
|         if (mutex->priority < best_priority) |  | ||||||
|             best_priority = mutex->priority; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     best_priority = std::min(best_priority, thread->nominal_priority); |  | ||||||
|     thread->SetPriority(best_priority); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /**
 |  | ||||||
|  * Elevate the mutex priority to the best priority |  | ||||||
|  * among the priorities of all its waiting threads. |  | ||||||
|  */ |  | ||||||
| static void UpdateMutexPriority(Mutex* mutex) { |  | ||||||
|     s32 best_priority = THREADPRIO_LOWEST; |  | ||||||
|     for (auto& waiter : mutex->GetWaitingThreads()) { |  | ||||||
|         if (waiter->current_priority < best_priority) |  | ||||||
|             best_priority = waiter->current_priority; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     if (best_priority != mutex->priority) { |  | ||||||
|         mutex->priority = best_priority; |  | ||||||
|         UpdateThreadPriority(mutex->holding_thread.get()); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void ReleaseThreadMutexes(Thread* thread) { | void ReleaseThreadMutexes(Thread* thread) { | ||||||
|     for (auto& mtx : thread->held_mutexes) { |     for (auto& mtx : thread->held_mutexes) { | ||||||
|         mtx->lock_count = 0; |         mtx->lock_count = 0; | ||||||
|  | @ -83,9 +51,7 @@ void Mutex::Acquire(Thread* thread) { | ||||||
|         priority = thread->current_priority; |         priority = thread->current_priority; | ||||||
|         thread->held_mutexes.insert(this); |         thread->held_mutexes.insert(this); | ||||||
|         holding_thread = thread; |         holding_thread = thread; | ||||||
| 
 |         thread->UpdatePriority(); | ||||||
|         UpdateThreadPriority(thread); |  | ||||||
| 
 |  | ||||||
|         Core::System::GetInstance().PrepareReschedule(); |         Core::System::GetInstance().PrepareReschedule(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -100,7 +66,7 @@ void Mutex::Release() { | ||||||
|         // Yield to the next thread only if we've fully released the mutex
 |         // Yield to the next thread only if we've fully released the mutex
 | ||||||
|         if (lock_count == 0) { |         if (lock_count == 0) { | ||||||
|             holding_thread->held_mutexes.erase(this); |             holding_thread->held_mutexes.erase(this); | ||||||
|             UpdateThreadPriority(holding_thread.get()); |             holding_thread->UpdatePriority(); | ||||||
|             holding_thread = nullptr; |             holding_thread = nullptr; | ||||||
|             WakeupAllWaitingThreads(); |             WakeupAllWaitingThreads(); | ||||||
|             Core::System::GetInstance().PrepareReschedule(); |             Core::System::GetInstance().PrepareReschedule(); | ||||||
|  | @ -110,12 +76,30 @@ void Mutex::Release() { | ||||||
| 
 | 
 | ||||||
| void Mutex::AddWaitingThread(SharedPtr<Thread> thread) { | void Mutex::AddWaitingThread(SharedPtr<Thread> thread) { | ||||||
|     WaitObject::AddWaitingThread(thread); |     WaitObject::AddWaitingThread(thread); | ||||||
|     UpdateMutexPriority(this); |     thread->pending_mutexes.insert(this); | ||||||
|  |     UpdatePriority(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Mutex::RemoveWaitingThread(Thread* thread) { | void Mutex::RemoveWaitingThread(Thread* thread) { | ||||||
|     WaitObject::RemoveWaitingThread(thread); |     WaitObject::RemoveWaitingThread(thread); | ||||||
|     UpdateMutexPriority(this); |     thread->pending_mutexes.erase(this); | ||||||
|  |     UpdatePriority(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Mutex::UpdatePriority() { | ||||||
|  |     if (!holding_thread) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|  |     s32 best_priority = THREADPRIO_LOWEST; | ||||||
|  |     for (auto& waiter : GetWaitingThreads()) { | ||||||
|  |         if (waiter->current_priority < best_priority) | ||||||
|  |             best_priority = waiter->current_priority; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (best_priority != priority) { | ||||||
|  |         priority = best_priority; | ||||||
|  |         holding_thread->UpdatePriority(); | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| } // namespace
 | } // namespace
 | ||||||
|  |  | ||||||
|  | @ -39,6 +39,12 @@ public: | ||||||
|     std::string name;                 ///< Name of mutex (optional)
 |     std::string name;                 ///< Name of mutex (optional)
 | ||||||
|     SharedPtr<Thread> holding_thread; ///< Thread that has acquired the mutex
 |     SharedPtr<Thread> holding_thread; ///< Thread that has acquired the mutex
 | ||||||
| 
 | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Elevate the mutex priority to the best priority | ||||||
|  |      * among the priorities of all its waiting threads. | ||||||
|  |      */ | ||||||
|  |     void UpdatePriority(); | ||||||
|  | 
 | ||||||
|     bool ShouldWait(Thread* thread) const override; |     bool ShouldWait(Thread* thread) const override; | ||||||
|     void Acquire(Thread* thread) override; |     void Acquire(Thread* thread) override; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -105,15 +105,15 @@ void Thread::Stop() { | ||||||
| 
 | 
 | ||||||
|     WakeupAllWaitingThreads(); |     WakeupAllWaitingThreads(); | ||||||
| 
 | 
 | ||||||
|     // Release all the mutexes that this thread holds
 |  | ||||||
|     ReleaseThreadMutexes(this); |  | ||||||
| 
 |  | ||||||
|     // Clean up any dangling references in objects that this thread was waiting for
 |     // Clean up any dangling references in objects that this thread was waiting for
 | ||||||
|     for (auto& wait_object : wait_objects) { |     for (auto& wait_object : wait_objects) { | ||||||
|         wait_object->RemoveWaitingThread(this); |         wait_object->RemoveWaitingThread(this); | ||||||
|     } |     } | ||||||
|     wait_objects.clear(); |     wait_objects.clear(); | ||||||
| 
 | 
 | ||||||
|  |     // Release all the mutexes that this thread holds
 | ||||||
|  |     ReleaseThreadMutexes(this); | ||||||
|  | 
 | ||||||
|     // Mark the TLS slot in the thread's page as free.
 |     // Mark the TLS slot in the thread's page as free.
 | ||||||
|     u32 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE; |     u32 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE; | ||||||
|     u32 tls_slot = |     u32 tls_slot = | ||||||
|  | @ -515,8 +515,21 @@ void Thread::SetPriority(s32 priority) { | ||||||
|     nominal_priority = current_priority = priority; |     nominal_priority = current_priority = priority; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void Thread::UpdatePriority() { | ||||||
|  |     s32 best_priority = nominal_priority; | ||||||
|  |     for (auto& mutex : held_mutexes) { | ||||||
|  |         if (mutex->priority < best_priority) | ||||||
|  |             best_priority = mutex->priority; | ||||||
|  |     } | ||||||
|  |     BoostPriority(best_priority); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void Thread::BoostPriority(s32 priority) { | void Thread::BoostPriority(s32 priority) { | ||||||
|     ready_queue.move(this, current_priority, priority); |     // If thread was ready, adjust queues
 | ||||||
|  |     if (status == THREADSTATUS_READY) | ||||||
|  |         ready_queue.move(this, current_priority, priority); | ||||||
|  |     else | ||||||
|  |         ready_queue.prepare(priority); | ||||||
|     current_priority = priority; |     current_priority = priority; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -89,6 +89,12 @@ public: | ||||||
|      */ |      */ | ||||||
|     void SetPriority(s32 priority); |     void SetPriority(s32 priority); | ||||||
| 
 | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Boost's a thread's priority to the best priority among the thread's held mutexes. | ||||||
|  |      * This prevents priority inversion via priority inheritance. | ||||||
|  |      */ | ||||||
|  |     void UpdatePriority(); | ||||||
|  | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Temporarily boosts the thread's priority until the next time it is scheduled |      * Temporarily boosts the thread's priority until the next time it is scheduled | ||||||
|      * @param priority The new priority |      * @param priority The new priority | ||||||
|  | @ -178,6 +184,9 @@ public: | ||||||
|     /// Mutexes currently held by this thread, which will be released when it exits.
 |     /// Mutexes currently held by this thread, which will be released when it exits.
 | ||||||
|     boost::container::flat_set<SharedPtr<Mutex>> held_mutexes; |     boost::container::flat_set<SharedPtr<Mutex>> held_mutexes; | ||||||
| 
 | 
 | ||||||
|  |     /// Mutexes that this thread is currently waiting for.
 | ||||||
|  |     boost::container::flat_set<SharedPtr<Mutex>> pending_mutexes; | ||||||
|  | 
 | ||||||
|     SharedPtr<Process> owner_process; ///< Process that owns this thread
 |     SharedPtr<Process> owner_process; ///< Process that owns this thread
 | ||||||
| 
 | 
 | ||||||
|     /// Objects that the thread is waiting on.
 |     /// Objects that the thread is waiting on.
 | ||||||
|  |  | ||||||
|  | @ -611,6 +611,12 @@ static ResultCode SetThreadPriority(Kernel::Handle handle, s32 priority) { | ||||||
|         return ERR_INVALID_HANDLE; |         return ERR_INVALID_HANDLE; | ||||||
| 
 | 
 | ||||||
|     thread->SetPriority(priority); |     thread->SetPriority(priority); | ||||||
|  |     thread->UpdatePriority(); | ||||||
|  | 
 | ||||||
|  |     // Update the mutexes that this thread is waiting for
 | ||||||
|  |     for (auto& mutex : thread->pending_mutexes) | ||||||
|  |         mutex->UpdatePriority(); | ||||||
|  | 
 | ||||||
|     Core::System::GetInstance().PrepareReschedule(); |     Core::System::GetInstance().PrepareReschedule(); | ||||||
|     return RESULT_SUCCESS; |     return RESULT_SUCCESS; | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue