|
|
|
@ -13,38 +13,6 @@ |
|
|
|
|
|
|
|
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) { |
|
|
|
for (auto& mtx : thread->held_mutexes) { |
|
|
|
mtx->lock_count = 0; |
|
|
|
@ -83,9 +51,7 @@ void Mutex::Acquire(Thread* thread) { |
|
|
|
priority = thread->current_priority; |
|
|
|
thread->held_mutexes.insert(this); |
|
|
|
holding_thread = thread; |
|
|
|
|
|
|
|
UpdateThreadPriority(thread); |
|
|
|
|
|
|
|
thread->UpdatePriority(); |
|
|
|
Core::System::GetInstance().PrepareReschedule(); |
|
|
|
} |
|
|
|
|
|
|
|
@ -100,7 +66,7 @@ void Mutex::Release() { |
|
|
|
// Yield to the next thread only if we've fully released the mutex
|
|
|
|
if (lock_count == 0) { |
|
|
|
holding_thread->held_mutexes.erase(this); |
|
|
|
UpdateThreadPriority(holding_thread.get()); |
|
|
|
holding_thread->UpdatePriority(); |
|
|
|
holding_thread = nullptr; |
|
|
|
WakeupAllWaitingThreads(); |
|
|
|
Core::System::GetInstance().PrepareReschedule(); |
|
|
|
@ -110,12 +76,30 @@ void Mutex::Release() { |
|
|
|
|
|
|
|
void Mutex::AddWaitingThread(SharedPtr<Thread> thread) { |
|
|
|
WaitObject::AddWaitingThread(thread); |
|
|
|
UpdateMutexPriority(this); |
|
|
|
thread->pending_mutexes.insert(this); |
|
|
|
UpdatePriority(); |
|
|
|
} |
|
|
|
|
|
|
|
void Mutex::RemoveWaitingThread(Thread* 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
|