From 38a90882a4230bb797ae3f6857e986d70f3bd265 Mon Sep 17 00:00:00 2001 From: Subv Date: Sun, 1 Jan 2017 11:57:02 -0500 Subject: Kernel/Synch: Do not attempt a reschedule on every syscall. Not all syscalls should cause reschedules, this commit attempts to remedy that, however, it still does not cover all cases. --- src/core/hle/kernel/mutex.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/core/hle/kernel/mutex.cpp') diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 736944bae..8d92a9b8e 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -84,6 +84,7 @@ void Mutex::Release() { if (lock_count == 0) { holding_thread->held_mutexes.erase(this); ResumeWaitingThread(this); + Core::System::GetInstance().PrepareReschedule(); } } } -- cgit v1.2.3 From e6a7723f2f4b62279cd4f6d4b48eb02a9b60ffb6 Mon Sep 17 00:00:00 2001 From: Subv Date: Sun, 1 Jan 2017 16:53:22 -0500 Subject: Kernel: Object ShouldWait and Acquire calls now take a thread as a parameter. This will be useful when implementing mutex priority inheritance. --- src/core/hle/kernel/mutex.cpp | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) (limited to 'src/core/hle/kernel/mutex.cpp') diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 8d92a9b8e..072e4e7c1 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -40,31 +40,19 @@ SharedPtr Mutex::Create(bool initial_locked, std::string name) { mutex->name = std::move(name); mutex->holding_thread = nullptr; - // Acquire mutex with current thread if initialized as locked... + // Acquire mutex with current thread if initialized as locked if (initial_locked) - mutex->Acquire(); + mutex->Acquire(GetCurrentThread()); return mutex; } -bool Mutex::ShouldWait() { - auto thread = GetCurrentThread(); - bool wait = lock_count > 0 && holding_thread != thread; - - // If the holding thread of the mutex is lower priority than this thread, that thread should - // temporarily inherit this thread's priority - if (wait && thread->current_priority < holding_thread->current_priority) - holding_thread->BoostPriority(thread->current_priority); - - return wait; -} - -void Mutex::Acquire() { - Acquire(GetCurrentThread()); +bool Mutex::ShouldWait(Thread* thread) const { + return lock_count > 0 && thread != holding_thread; } -void Mutex::Acquire(SharedPtr thread) { - ASSERT_MSG(!ShouldWait(), "object unavailable!"); +void Mutex::Acquire(Thread* thread) { + ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); // Actually "acquire" the mutex only if we don't already have it... if (lock_count == 0) { -- cgit v1.2.3 From 7abf1853907fe086753df0031262b668a2da88b0 Mon Sep 17 00:00:00 2001 From: Subv Date: Sun, 1 Jan 2017 16:59:30 -0500 Subject: Kernel/Mutex: Implemented priority inheritance. The implementation is based on reverse engineering of the 3DS's kernel. A mutex holder's priority will be temporarily boosted to the best priority among any threads that want to acquire any of its held mutexes. When the holder releases the mutex, it's priority will be boosted to the best priority among the threads that want to acquire any of its remaining held mutexes. --- src/core/hle/kernel/mutex.cpp | 58 +++++++++++++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 13 deletions(-) (limited to 'src/core/hle/kernel/mutex.cpp') diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 072e4e7c1..e83717e80 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -6,6 +6,7 @@ #include #include #include "common/assert.h" +#include "core/core.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/mutex.h" #include "core/hle/kernel/thread.h" @@ -13,19 +14,25 @@ namespace Kernel { /** - * Resumes a thread waiting for the specified mutex - * @param mutex The mutex that some thread is waiting on + * 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 ResumeWaitingThread(Mutex* mutex) { - // Reset mutex lock thread handle, nothing is waiting - mutex->lock_count = 0; - mutex->holding_thread = nullptr; - mutex->WakeupAllWaitingThreads(); +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); } void ReleaseThreadMutexes(Thread* thread) { for (auto& mtx : thread->held_mutexes) { - ResumeWaitingThread(mtx.get()); + mtx->lock_count = 0; + mtx->holding_thread = nullptr; + mtx->WakeupAllWaitingThreads(); } thread->held_mutexes.clear(); } @@ -54,27 +61,52 @@ bool Mutex::ShouldWait(Thread* thread) const { void Mutex::Acquire(Thread* thread) { ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); - // Actually "acquire" the mutex only if we don't already have it... + // Actually "acquire" the mutex only if we don't already have it if (lock_count == 0) { + priority = thread->current_priority; thread->held_mutexes.insert(this); - holding_thread = std::move(thread); + holding_thread = thread; + + UpdateThreadPriority(thread); + + Core::System::GetInstance().PrepareReschedule(); } lock_count++; } void Mutex::Release() { - // Only release if the mutex is held... + // Only release if the mutex is held if (lock_count > 0) { lock_count--; - // 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) { holding_thread->held_mutexes.erase(this); - ResumeWaitingThread(this); + UpdateThreadPriority(holding_thread.get()); + holding_thread = nullptr; + WakeupAllWaitingThreads(); Core::System::GetInstance().PrepareReschedule(); } } } +void Mutex::AddWaitingThread(SharedPtr thread) { + WaitObject::AddWaitingThread(thread); + + // 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) { + priority = best_priority; + UpdateThreadPriority(holding_thread.get()); + } +} + } // namespace -- cgit v1.2.3 From b6a0355568ee327bef8957b9a2498897b96e1278 Mon Sep 17 00:00:00 2001 From: Subv Date: Mon, 2 Jan 2017 13:53:10 -0500 Subject: Kernel/Mutex: Update a mutex priority when a thread stops waiting on it. --- src/core/hle/kernel/mutex.cpp | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) (limited to 'src/core/hle/kernel/mutex.cpp') diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index e83717e80..84ff65150 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -28,6 +28,23 @@ static void UpdateThreadPriority(Thread* thread) { 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; @@ -93,20 +110,12 @@ void Mutex::Release() { void Mutex::AddWaitingThread(SharedPtr 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) { - priority = best_priority; - UpdateThreadPriority(holding_thread.get()); - } +void Mutex::RemoveWaitingThread(Thread* thread) { + WaitObject::RemoveWaitingThread(thread); + UpdateMutexPriority(this); } } // namespace -- cgit v1.2.3 From d3ff5b91e14356912589f9bac47fccbe79e07279 Mon Sep 17 00:00:00 2001 From: Subv Date: Mon, 2 Jan 2017 19:38:08 -0500 Subject: Kernel/Mutex: Propagate thread priority changes to other threads inheriting the priority via mutexes --- src/core/hle/kernel/mutex.cpp | 60 ++++++++++++++++--------------------------- 1 file changed, 22 insertions(+), 38 deletions(-) (limited to 'src/core/hle/kernel/mutex.cpp') diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 84ff65150..cef961289 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -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) { 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 -- cgit v1.2.3