Kernel/Mutex: Update a mutex priority when a thread stops waiting on it.
This commit is contained in:
parent
7abf185390
commit
b6a0355568
|
@ -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 {
|
||||||
|
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;
|
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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
|
||||||
// among the priorities of all its waiting threads.
|
|
||||||
|
|
||||||
s32 best_priority = THREADPRIO_LOWEST;
|
|
||||||
for (auto& waiter : GetWaitingThreads()) {
|
|
||||||
if (waiter->current_priority < best_priority)
|
|
||||||
best_priority = waiter->current_priority;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (best_priority != priority) {
|
void Mutex::RemoveWaitingThread(Thread* thread) {
|
||||||
priority = best_priority;
|
WaitObject::RemoveWaitingThread(thread);
|
||||||
UpdateThreadPriority(holding_thread.get());
|
UpdateMutexPriority(this);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Reference in New Issue