diff options
| author | 2017-01-02 19:38:08 -0500 | |
|---|---|---|
| committer | 2017-01-04 15:58:48 -0500 | |
| commit | d3ff5b91e14356912589f9bac47fccbe79e07279 (patch) | |
| tree | e35c698d40539e360981a61b38f8fbb60aee7418 /src/core/hle/kernel | |
| parent | Kernel/Mutex: Update a mutex priority when a thread stops waiting on it. (diff) | |
| download | yuzu-d3ff5b91e14356912589f9bac47fccbe79e07279.tar.gz yuzu-d3ff5b91e14356912589f9bac47fccbe79e07279.tar.xz yuzu-d3ff5b91e14356912589f9bac47fccbe79e07279.zip | |
Kernel/Mutex: Propagate thread priority changes to other threads inheriting the priority via mutexes
Diffstat (limited to 'src/core/hle/kernel')
| -rw-r--r-- | src/core/hle/kernel/mutex.cpp | 60 | ||||
| -rw-r--r-- | src/core/hle/kernel/mutex.h | 6 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.cpp | 21 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.h | 9 |
4 files changed, 54 insertions, 42 deletions
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 @@ | |||
| 13 | 13 | ||
| 14 | namespace Kernel { | 14 | namespace Kernel { |
| 15 | 15 | ||
| 16 | /** | ||
| 17 | * Boost's a thread's priority to the best priority among the thread's held mutexes. | ||
| 18 | * This prevents priority inversion via priority inheritance. | ||
| 19 | */ | ||
| 20 | static void UpdateThreadPriority(Thread* thread) { | ||
| 21 | s32 best_priority = THREADPRIO_LOWEST; | ||
| 22 | for (auto& mutex : thread->held_mutexes) { | ||
| 23 | if (mutex->priority < best_priority) | ||
| 24 | best_priority = mutex->priority; | ||
| 25 | } | ||
| 26 | |||
| 27 | best_priority = std::min(best_priority, thread->nominal_priority); | ||
| 28 | thread->SetPriority(best_priority); | ||
| 29 | } | ||
| 30 | |||
| 31 | /** | ||
| 32 | * Elevate the mutex priority to the best priority | ||
| 33 | * among the priorities of all its waiting threads. | ||
| 34 | */ | ||
| 35 | static void UpdateMutexPriority(Mutex* mutex) { | ||
| 36 | s32 best_priority = THREADPRIO_LOWEST; | ||
| 37 | for (auto& waiter : mutex->GetWaitingThreads()) { | ||
| 38 | if (waiter->current_priority < best_priority) | ||
| 39 | best_priority = waiter->current_priority; | ||
| 40 | } | ||
| 41 | |||
| 42 | if (best_priority != mutex->priority) { | ||
| 43 | mutex->priority = best_priority; | ||
| 44 | UpdateThreadPriority(mutex->holding_thread.get()); | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | void ReleaseThreadMutexes(Thread* thread) { | 16 | void ReleaseThreadMutexes(Thread* thread) { |
| 49 | for (auto& mtx : thread->held_mutexes) { | 17 | for (auto& mtx : thread->held_mutexes) { |
| 50 | mtx->lock_count = 0; | 18 | mtx->lock_count = 0; |
| @@ -83,9 +51,7 @@ void Mutex::Acquire(Thread* thread) { | |||
| 83 | priority = thread->current_priority; | 51 | priority = thread->current_priority; |
| 84 | thread->held_mutexes.insert(this); | 52 | thread->held_mutexes.insert(this); |
| 85 | holding_thread = thread; | 53 | holding_thread = thread; |
| 86 | 54 | thread->UpdatePriority(); | |
| 87 | UpdateThreadPriority(thread); | ||
| 88 | |||
| 89 | Core::System::GetInstance().PrepareReschedule(); | 55 | Core::System::GetInstance().PrepareReschedule(); |
| 90 | } | 56 | } |
| 91 | 57 | ||
| @@ -100,7 +66,7 @@ void Mutex::Release() { | |||
| 100 | // Yield to the next thread only if we've fully released the mutex | 66 | // Yield to the next thread only if we've fully released the mutex |
| 101 | if (lock_count == 0) { | 67 | if (lock_count == 0) { |
| 102 | holding_thread->held_mutexes.erase(this); | 68 | holding_thread->held_mutexes.erase(this); |
| 103 | UpdateThreadPriority(holding_thread.get()); | 69 | holding_thread->UpdatePriority(); |
| 104 | holding_thread = nullptr; | 70 | holding_thread = nullptr; |
| 105 | WakeupAllWaitingThreads(); | 71 | WakeupAllWaitingThreads(); |
| 106 | Core::System::GetInstance().PrepareReschedule(); | 72 | Core::System::GetInstance().PrepareReschedule(); |
| @@ -110,12 +76,30 @@ void Mutex::Release() { | |||
| 110 | 76 | ||
| 111 | void Mutex::AddWaitingThread(SharedPtr<Thread> thread) { | 77 | void Mutex::AddWaitingThread(SharedPtr<Thread> thread) { |
| 112 | WaitObject::AddWaitingThread(thread); | 78 | WaitObject::AddWaitingThread(thread); |
| 113 | UpdateMutexPriority(this); | 79 | thread->pending_mutexes.insert(this); |
| 80 | UpdatePriority(); | ||
| 114 | } | 81 | } |
| 115 | 82 | ||
| 116 | void Mutex::RemoveWaitingThread(Thread* thread) { | 83 | void Mutex::RemoveWaitingThread(Thread* thread) { |
| 117 | WaitObject::RemoveWaitingThread(thread); | 84 | WaitObject::RemoveWaitingThread(thread); |
| 118 | UpdateMutexPriority(this); | 85 | thread->pending_mutexes.erase(this); |
| 86 | UpdatePriority(); | ||
| 87 | } | ||
| 88 | |||
| 89 | void Mutex::UpdatePriority() { | ||
| 90 | if (!holding_thread) | ||
| 91 | return; | ||
| 92 | |||
| 93 | s32 best_priority = THREADPRIO_LOWEST; | ||
| 94 | for (auto& waiter : GetWaitingThreads()) { | ||
| 95 | if (waiter->current_priority < best_priority) | ||
| 96 | best_priority = waiter->current_priority; | ||
| 97 | } | ||
| 98 | |||
| 99 | if (best_priority != priority) { | ||
| 100 | priority = best_priority; | ||
| 101 | holding_thread->UpdatePriority(); | ||
| 102 | } | ||
| 119 | } | 103 | } |
| 120 | 104 | ||
| 121 | } // namespace | 105 | } // namespace |
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index 31f920516..c57adf400 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h | |||
| @@ -39,6 +39,12 @@ public: | |||
| 39 | std::string name; ///< Name of mutex (optional) | 39 | std::string name; ///< Name of mutex (optional) |
| 40 | SharedPtr<Thread> holding_thread; ///< Thread that has acquired the mutex | 40 | SharedPtr<Thread> holding_thread; ///< Thread that has acquired the mutex |
| 41 | 41 | ||
| 42 | /** | ||
| 43 | * Elevate the mutex priority to the best priority | ||
| 44 | * among the priorities of all its waiting threads. | ||
| 45 | */ | ||
| 46 | void UpdatePriority(); | ||
| 47 | |||
| 42 | bool ShouldWait(Thread* thread) const override; | 48 | bool ShouldWait(Thread* thread) const override; |
| 43 | void Acquire(Thread* thread) override; | 49 | void Acquire(Thread* thread) override; |
| 44 | 50 | ||
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index d44010824..3a5a67450 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -105,15 +105,15 @@ void Thread::Stop() { | |||
| 105 | 105 | ||
| 106 | WakeupAllWaitingThreads(); | 106 | WakeupAllWaitingThreads(); |
| 107 | 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 | 108 | // Clean up any dangling references in objects that this thread was waiting for |
| 112 | for (auto& wait_object : wait_objects) { | 109 | for (auto& wait_object : wait_objects) { |
| 113 | wait_object->RemoveWaitingThread(this); | 110 | wait_object->RemoveWaitingThread(this); |
| 114 | } | 111 | } |
| 115 | wait_objects.clear(); | 112 | wait_objects.clear(); |
| 116 | 113 | ||
| 114 | // Release all the mutexes that this thread holds | ||
| 115 | ReleaseThreadMutexes(this); | ||
| 116 | |||
| 117 | // Mark the TLS slot in the thread's page as free. | 117 | // Mark the TLS slot in the thread's page as free. |
| 118 | u32 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE; | 118 | u32 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE; |
| 119 | u32 tls_slot = | 119 | u32 tls_slot = |
| @@ -515,8 +515,21 @@ void Thread::SetPriority(s32 priority) { | |||
| 515 | nominal_priority = current_priority = priority; | 515 | nominal_priority = current_priority = priority; |
| 516 | } | 516 | } |
| 517 | 517 | ||
| 518 | void Thread::UpdatePriority() { | ||
| 519 | s32 best_priority = nominal_priority; | ||
| 520 | for (auto& mutex : held_mutexes) { | ||
| 521 | if (mutex->priority < best_priority) | ||
| 522 | best_priority = mutex->priority; | ||
| 523 | } | ||
| 524 | BoostPriority(best_priority); | ||
| 525 | } | ||
| 526 | |||
| 518 | void Thread::BoostPriority(s32 priority) { | 527 | void Thread::BoostPriority(s32 priority) { |
| 519 | ready_queue.move(this, current_priority, priority); | 528 | // If thread was ready, adjust queues |
| 529 | if (status == THREADSTATUS_READY) | ||
| 530 | ready_queue.move(this, current_priority, priority); | ||
| 531 | else | ||
| 532 | ready_queue.prepare(priority); | ||
| 520 | current_priority = priority; | 533 | current_priority = priority; |
| 521 | } | 534 | } |
| 522 | 535 | ||
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index f2bc1ec9c..e2f0cc831 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -90,6 +90,12 @@ public: | |||
| 90 | void SetPriority(s32 priority); | 90 | void SetPriority(s32 priority); |
| 91 | 91 | ||
| 92 | /** | 92 | /** |
| 93 | * Boost's a thread's priority to the best priority among the thread's held mutexes. | ||
| 94 | * This prevents priority inversion via priority inheritance. | ||
| 95 | */ | ||
| 96 | void UpdatePriority(); | ||
| 97 | |||
| 98 | /** | ||
| 93 | * Temporarily boosts the thread's priority until the next time it is scheduled | 99 | * Temporarily boosts the thread's priority until the next time it is scheduled |
| 94 | * @param priority The new priority | 100 | * @param priority The new priority |
| 95 | */ | 101 | */ |
| @@ -178,6 +184,9 @@ public: | |||
| 178 | /// Mutexes currently held by this thread, which will be released when it exits. | 184 | /// Mutexes currently held by this thread, which will be released when it exits. |
| 179 | boost::container::flat_set<SharedPtr<Mutex>> held_mutexes; | 185 | boost::container::flat_set<SharedPtr<Mutex>> held_mutexes; |
| 180 | 186 | ||
| 187 | /// Mutexes that this thread is currently waiting for. | ||
| 188 | boost::container::flat_set<SharedPtr<Mutex>> pending_mutexes; | ||
| 189 | |||
| 181 | SharedPtr<Process> owner_process; ///< Process that owns this thread | 190 | SharedPtr<Process> owner_process; ///< Process that owns this thread |
| 182 | 191 | ||
| 183 | /// Objects that the thread is waiting on. | 192 | /// Objects that the thread is waiting on. |