diff options
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); |