Kernel/Mutex: Update a mutex priority when a thread stops waiting on it.

This commit is contained in:
Subv 2017-01-02 13:53:10 -05:00
parent 7abf185390
commit b6a0355568
5 changed files with 42 additions and 24 deletions

View file

@ -3,7 +3,6 @@
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <algorithm> #include <algorithm>
#include <boost/range/algorithm_ext/erase.hpp>
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/hle/config_mem.h" #include "core/hle/config_mem.h"
@ -34,10 +33,17 @@ void WaitObject::RemoveWaitingThread(Thread* thread) {
SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
// Remove the threads that are ready or already running from our waitlist // Remove the threads that are ready or already running from our waitlist
boost::range::remove_erase_if(waiting_threads, [](const SharedPtr<Thread>& thread) { auto to_remove = waiting_threads.end();
return thread->status == THREADSTATUS_RUNNING || thread->status == THREADSTATUS_READY || do {
thread->status == THREADSTATUS_DEAD; to_remove = std::find_if(waiting_threads.begin(), waiting_threads.end(),
}); [](const SharedPtr<Thread>& thread) {
return thread->status == THREADSTATUS_RUNNING ||
thread->status == THREADSTATUS_READY ||
thread->status == THREADSTATUS_DEAD;
});
// Call RemoveWaitingThread so that child classes can override the behavior.
RemoveWaitingThread(to_remove->get());
} while (to_remove != waiting_threads.end());
Thread* candidate = nullptr; Thread* candidate = nullptr;
s32 candidate_priority = THREADPRIO_LOWEST + 1; s32 candidate_priority = THREADPRIO_LOWEST + 1;
@ -49,9 +55,10 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
if (ShouldWait(thread.get())) if (ShouldWait(thread.get()))
continue; continue;
bool ready_to_run = bool ready_to_run = std::none_of(thread->wait_objects.begin(), thread->wait_objects.end(),
std::none_of(thread->wait_objects.begin(), thread->wait_objects.end(), [&thread](const SharedPtr<WaitObject>& object) {
[&thread](const SharedPtr<WaitObject>& object) { return object->ShouldWait(thread.get()); }); return object->ShouldWait(thread.get());
});
if (ready_to_run) { if (ready_to_run) {
candidate = thread.get(); candidate = thread.get();
candidate_priority = thread->current_priority; candidate_priority = thread->current_priority;

View file

@ -151,7 +151,7 @@ public:
* Removes a thread from waiting on this object (e.g. if it was resumed already) * Removes a thread from waiting on this object (e.g. if it was resumed already)
* @param thread Pointer to thread to remove * @param thread Pointer to thread to remove
*/ */
void RemoveWaitingThread(Thread* thread); virtual void RemoveWaitingThread(Thread* thread);
/** /**
* Wake up all threads waiting on this object that can be awoken, in priority order, * Wake up all threads waiting on this object that can be awoken, in priority order,

View file

@ -28,6 +28,23 @@ static void UpdateThreadPriority(Thread* thread) {
thread->SetPriority(best_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;
@ -93,20 +110,12 @@ void Mutex::Release() {
void Mutex::AddWaitingThread(SharedPtr<Thread> thread) { void Mutex::AddWaitingThread(SharedPtr<Thread> thread) {
WaitObject::AddWaitingThread(thread); WaitObject::AddWaitingThread(thread);
UpdateMutexPriority(this);
}
// Elevate the mutex priority to the best priority void Mutex::RemoveWaitingThread(Thread* thread) {
// among the priorities of all its waiting threads. WaitObject::RemoveWaitingThread(thread);
UpdateMutexPriority(this);
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;
UpdateThreadPriority(holding_thread.get());
}
} }
} // namespace } // namespace

View file

@ -43,6 +43,7 @@ public:
void Acquire(Thread* thread) override; void Acquire(Thread* thread) override;
void AddWaitingThread(SharedPtr<Thread> thread) override; void AddWaitingThread(SharedPtr<Thread> thread) override;
void RemoveWaitingThread(Thread* thread) override;
void Release(); void Release();

View file

@ -373,8 +373,9 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha
return ERR_SYNC_TIMEOUT; return ERR_SYNC_TIMEOUT;
} else { } else {
// Find the first object that is acquirable in the provided list of objects // Find the first object that is acquirable in the provided list of objects
auto itr = std::find_if(objects.begin(), objects.end(), auto itr = std::find_if(objects.begin(), objects.end(), [thread](const ObjectPtr& object) {
[thread](const ObjectPtr& object) { return !object->ShouldWait(thread); }); return !object->ShouldWait(thread);
});
if (itr != objects.end()) { if (itr != objects.end()) {
// We found a ready object, acquire it and set the result value // We found a ready object, acquire it and set the result value