mirror of
https://github.com/citra-emu/citra.git
synced 2024-12-18 17:30:05 +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
@ -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) {
|
||||||
|
// If thread was ready, adjust queues
|
||||||
|
if (status == THREADSTATUS_READY)
|
||||||
ready_queue.move(this, current_priority, priority);
|
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…
Reference in New Issue
Block a user