diff options
| author | 2017-01-01 16:59:30 -0500 | |
|---|---|---|
| committer | 2017-01-04 15:58:46 -0500 | |
| commit | 7abf1853907fe086753df0031262b668a2da88b0 (patch) | |
| tree | 033c38e1d98f209c32c1378419468212729877b4 /src/core/hle/kernel | |
| parent | Kernel: Object ShouldWait and Acquire calls now take a thread as a parameter. (diff) | |
| download | yuzu-7abf1853907fe086753df0031262b668a2da88b0.tar.gz yuzu-7abf1853907fe086753df0031262b668a2da88b0.tar.xz yuzu-7abf1853907fe086753df0031262b668a2da88b0.zip | |
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.
Diffstat (limited to 'src/core/hle/kernel')
| -rw-r--r-- | src/core/hle/kernel/kernel.h | 2 | ||||
| -rw-r--r-- | src/core/hle/kernel/mutex.cpp | 58 | ||||
| -rw-r--r-- | src/core/hle/kernel/mutex.h | 7 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.cpp | 6 |
4 files changed, 51 insertions, 22 deletions
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 67eae93f2..2680f89c9 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h | |||
| @@ -145,7 +145,7 @@ public: | |||
| 145 | * Add a thread to wait on this object | 145 | * Add a thread to wait on this object |
| 146 | * @param thread Pointer to thread to add | 146 | * @param thread Pointer to thread to add |
| 147 | */ | 147 | */ |
| 148 | void AddWaitingThread(SharedPtr<Thread> thread); | 148 | virtual void AddWaitingThread(SharedPtr<Thread> thread); |
| 149 | 149 | ||
| 150 | /** | 150 | /** |
| 151 | * Removes a thread from waiting on this object (e.g. if it was resumed already) | 151 | * Removes a thread from waiting on this object (e.g. if it was resumed already) |
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 @@ | |||
| 6 | #include <vector> | 6 | #include <vector> |
| 7 | #include <boost/range/algorithm_ext/erase.hpp> | 7 | #include <boost/range/algorithm_ext/erase.hpp> |
| 8 | #include "common/assert.h" | 8 | #include "common/assert.h" |
| 9 | #include "core/core.h" | ||
| 9 | #include "core/hle/kernel/kernel.h" | 10 | #include "core/hle/kernel/kernel.h" |
| 10 | #include "core/hle/kernel/mutex.h" | 11 | #include "core/hle/kernel/mutex.h" |
| 11 | #include "core/hle/kernel/thread.h" | 12 | #include "core/hle/kernel/thread.h" |
| @@ -13,19 +14,25 @@ | |||
| 13 | namespace Kernel { | 14 | namespace Kernel { |
| 14 | 15 | ||
| 15 | /** | 16 | /** |
| 16 | * Resumes a thread waiting for the specified mutex | 17 | * Boost's a thread's priority to the best priority among the thread's held mutexes. |
| 17 | * @param mutex The mutex that some thread is waiting on | 18 | * This prevents priority inversion via priority inheritance. |
| 18 | */ | 19 | */ |
| 19 | static void ResumeWaitingThread(Mutex* mutex) { | 20 | static void UpdateThreadPriority(Thread* thread) { |
| 20 | // Reset mutex lock thread handle, nothing is waiting | 21 | s32 best_priority = THREADPRIO_LOWEST; |
| 21 | mutex->lock_count = 0; | 22 | for (auto& mutex : thread->held_mutexes) { |
| 22 | mutex->holding_thread = nullptr; | 23 | if (mutex->priority < best_priority) |
| 23 | mutex->WakeupAllWaitingThreads(); | 24 | best_priority = mutex->priority; |
| 25 | } | ||
| 26 | |||
| 27 | best_priority = std::min(best_priority, thread->nominal_priority); | ||
| 28 | thread->SetPriority(best_priority); | ||
| 24 | } | 29 | } |
| 25 | 30 | ||
| 26 | void ReleaseThreadMutexes(Thread* thread) { | 31 | void ReleaseThreadMutexes(Thread* thread) { |
| 27 | for (auto& mtx : thread->held_mutexes) { | 32 | for (auto& mtx : thread->held_mutexes) { |
| 28 | ResumeWaitingThread(mtx.get()); | 33 | mtx->lock_count = 0; |
| 34 | mtx->holding_thread = nullptr; | ||
| 35 | mtx->WakeupAllWaitingThreads(); | ||
| 29 | } | 36 | } |
| 30 | thread->held_mutexes.clear(); | 37 | thread->held_mutexes.clear(); |
| 31 | } | 38 | } |
| @@ -54,27 +61,52 @@ bool Mutex::ShouldWait(Thread* thread) const { | |||
| 54 | void Mutex::Acquire(Thread* thread) { | 61 | void Mutex::Acquire(Thread* thread) { |
| 55 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); | 62 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); |
| 56 | 63 | ||
| 57 | // Actually "acquire" the mutex only if we don't already have it... | 64 | // Actually "acquire" the mutex only if we don't already have it |
| 58 | if (lock_count == 0) { | 65 | if (lock_count == 0) { |
| 66 | priority = thread->current_priority; | ||
| 59 | thread->held_mutexes.insert(this); | 67 | thread->held_mutexes.insert(this); |
| 60 | holding_thread = std::move(thread); | 68 | holding_thread = thread; |
| 69 | |||
| 70 | UpdateThreadPriority(thread); | ||
| 71 | |||
| 72 | Core::System::GetInstance().PrepareReschedule(); | ||
| 61 | } | 73 | } |
| 62 | 74 | ||
| 63 | lock_count++; | 75 | lock_count++; |
| 64 | } | 76 | } |
| 65 | 77 | ||
| 66 | void Mutex::Release() { | 78 | void Mutex::Release() { |
| 67 | // Only release if the mutex is held... | 79 | // Only release if the mutex is held |
| 68 | if (lock_count > 0) { | 80 | if (lock_count > 0) { |
| 69 | lock_count--; | 81 | lock_count--; |
| 70 | 82 | ||
| 71 | // Yield to the next thread only if we've fully released the mutex... | 83 | // Yield to the next thread only if we've fully released the mutex |
| 72 | if (lock_count == 0) { | 84 | if (lock_count == 0) { |
| 73 | holding_thread->held_mutexes.erase(this); | 85 | holding_thread->held_mutexes.erase(this); |
| 74 | ResumeWaitingThread(this); | 86 | UpdateThreadPriority(holding_thread.get()); |
| 87 | holding_thread = nullptr; | ||
| 88 | WakeupAllWaitingThreads(); | ||
| 75 | Core::System::GetInstance().PrepareReschedule(); | 89 | Core::System::GetInstance().PrepareReschedule(); |
| 76 | } | 90 | } |
| 77 | } | 91 | } |
| 78 | } | 92 | } |
| 79 | 93 | ||
| 94 | void Mutex::AddWaitingThread(SharedPtr<Thread> thread) { | ||
| 95 | WaitObject::AddWaitingThread(thread); | ||
| 96 | |||
| 97 | // Elevate the mutex priority to the best priority | ||
| 98 | // among the priorities of all its waiting threads. | ||
| 99 | |||
| 100 | s32 best_priority = THREADPRIO_LOWEST; | ||
| 101 | for (auto& waiter : GetWaitingThreads()) { | ||
| 102 | if (waiter->current_priority < best_priority) | ||
| 103 | best_priority = waiter->current_priority; | ||
| 104 | } | ||
| 105 | |||
| 106 | if (best_priority != priority) { | ||
| 107 | priority = best_priority; | ||
| 108 | UpdateThreadPriority(holding_thread.get()); | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 80 | } // namespace | 112 | } // namespace |
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index 98b3d40b5..3e6adeb17 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h | |||
| @@ -35,18 +35,15 @@ public: | |||
| 35 | } | 35 | } |
| 36 | 36 | ||
| 37 | int lock_count; ///< Number of times the mutex has been acquired | 37 | int lock_count; ///< Number of times the mutex has been acquired |
| 38 | u32 priority; ///< The priority of the mutex, used for priority inheritance. | ||
| 38 | std::string name; ///< Name of mutex (optional) | 39 | std::string name; ///< Name of mutex (optional) |
| 39 | SharedPtr<Thread> holding_thread; ///< Thread that has acquired the mutex | 40 | SharedPtr<Thread> holding_thread; ///< Thread that has acquired the mutex |
| 40 | 41 | ||
| 41 | bool ShouldWait(Thread* thread) const override; | 42 | bool ShouldWait(Thread* thread) const override; |
| 42 | void Acquire(Thread* thread) override; | 43 | void Acquire(Thread* thread) override; |
| 43 | 44 | ||
| 45 | void AddWaitingThread(SharedPtr<Thread> thread) override; | ||
| 44 | 46 | ||
| 45 | /** | ||
| 46 | * Acquires the specified mutex for the specified thread | ||
| 47 | * @param thread Thread that will acquire the mutex | ||
| 48 | */ | ||
| 49 | void Acquire(SharedPtr<Thread> thread); | ||
| 50 | void Release(); | 47 | void Release(); |
| 51 | 48 | ||
| 52 | private: | 49 | private: |
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 7d03a2cf7..d44010824 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -90,9 +90,6 @@ static bool CheckWait_AddressArbiter(const Thread* thread, VAddr wait_address) { | |||
| 90 | } | 90 | } |
| 91 | 91 | ||
| 92 | void Thread::Stop() { | 92 | void Thread::Stop() { |
| 93 | // Release all the mutexes that this thread holds | ||
| 94 | ReleaseThreadMutexes(this); | ||
| 95 | |||
| 96 | // Cancel any outstanding wakeup events for this thread | 93 | // Cancel any outstanding wakeup events for this thread |
| 97 | CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle); | 94 | CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle); |
| 98 | wakeup_callback_handle_table.Close(callback_handle); | 95 | wakeup_callback_handle_table.Close(callback_handle); |
| @@ -108,6 +105,9 @@ void Thread::Stop() { | |||
| 108 | 105 | ||
| 109 | WakeupAllWaitingThreads(); | 106 | WakeupAllWaitingThreads(); |
| 110 | 107 | ||
| 108 | // Release all the mutexes that this thread holds | ||
| 109 | ReleaseThreadMutexes(this); | ||
| 110 | |||
| 111 | // Clean up any dangling references in objects that this thread was waiting for | 111 | // Clean up any dangling references in objects that this thread was waiting for |
| 112 | for (auto& wait_object : wait_objects) { | 112 | for (auto& wait_object : wait_objects) { |
| 113 | wait_object->RemoveWaitingThread(this); | 113 | wait_object->RemoveWaitingThread(this); |