diff options
| author | 2017-01-05 12:55:01 -0500 | |
|---|---|---|
| committer | 2017-01-05 12:55:01 -0500 | |
| commit | f20d872643654c574f73a263f032613046900f07 (patch) | |
| tree | 021284c18034d053c81928fa19d2efb6658451fb /src/core/hle/kernel/mutex.cpp | |
| parent | Merge pull request #2407 from jroweboy/nightly-deploy (diff) | |
| parent | Kernel: Add some asserts to enforce the invariants in the scheduler. (diff) | |
| download | yuzu-f20d872643654c574f73a263f032613046900f07.tar.gz yuzu-f20d872643654c574f73a263f032613046900f07.tar.xz yuzu-f20d872643654c574f73a263f032613046900f07.zip | |
Merge pull request #2393 from Subv/synch
Kernel: Mutex priority inheritance and synchronization improvements.
Diffstat (limited to 'src/core/hle/kernel/mutex.cpp')
| -rw-r--r-- | src/core/hle/kernel/mutex.cpp | 84 |
1 files changed, 49 insertions, 35 deletions
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 736944bae..cef961289 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp | |||
| @@ -6,26 +6,18 @@ | |||
| 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" |
| 12 | 13 | ||
| 13 | namespace Kernel { | 14 | namespace Kernel { |
| 14 | 15 | ||
| 15 | /** | ||
| 16 | * Resumes a thread waiting for the specified mutex | ||
| 17 | * @param mutex The mutex that some thread is waiting on | ||
| 18 | */ | ||
| 19 | static void ResumeWaitingThread(Mutex* mutex) { | ||
| 20 | // Reset mutex lock thread handle, nothing is waiting | ||
| 21 | mutex->lock_count = 0; | ||
| 22 | mutex->holding_thread = nullptr; | ||
| 23 | mutex->WakeupAllWaitingThreads(); | ||
| 24 | } | ||
| 25 | |||
| 26 | void ReleaseThreadMutexes(Thread* thread) { | 16 | void ReleaseThreadMutexes(Thread* thread) { |
| 27 | for (auto& mtx : thread->held_mutexes) { | 17 | for (auto& mtx : thread->held_mutexes) { |
| 28 | ResumeWaitingThread(mtx.get()); | 18 | mtx->lock_count = 0; |
| 19 | mtx->holding_thread = nullptr; | ||
| 20 | mtx->WakeupAllWaitingThreads(); | ||
| 29 | } | 21 | } |
| 30 | thread->held_mutexes.clear(); | 22 | thread->held_mutexes.clear(); |
| 31 | } | 23 | } |
| @@ -40,52 +32,74 @@ SharedPtr<Mutex> Mutex::Create(bool initial_locked, std::string name) { | |||
| 40 | mutex->name = std::move(name); | 32 | mutex->name = std::move(name); |
| 41 | mutex->holding_thread = nullptr; | 33 | mutex->holding_thread = nullptr; |
| 42 | 34 | ||
| 43 | // Acquire mutex with current thread if initialized as locked... | 35 | // Acquire mutex with current thread if initialized as locked |
| 44 | if (initial_locked) | 36 | if (initial_locked) |
| 45 | mutex->Acquire(); | 37 | mutex->Acquire(GetCurrentThread()); |
| 46 | 38 | ||
| 47 | return mutex; | 39 | return mutex; |
| 48 | } | 40 | } |
| 49 | 41 | ||
| 50 | bool Mutex::ShouldWait() { | 42 | bool Mutex::ShouldWait(Thread* thread) const { |
| 51 | auto thread = GetCurrentThread(); | 43 | return lock_count > 0 && thread != holding_thread; |
| 52 | bool wait = lock_count > 0 && holding_thread != thread; | ||
| 53 | |||
| 54 | // If the holding thread of the mutex is lower priority than this thread, that thread should | ||
| 55 | // temporarily inherit this thread's priority | ||
| 56 | if (wait && thread->current_priority < holding_thread->current_priority) | ||
| 57 | holding_thread->BoostPriority(thread->current_priority); | ||
| 58 | |||
| 59 | return wait; | ||
| 60 | } | ||
| 61 | |||
| 62 | void Mutex::Acquire() { | ||
| 63 | Acquire(GetCurrentThread()); | ||
| 64 | } | 44 | } |
| 65 | 45 | ||
| 66 | void Mutex::Acquire(SharedPtr<Thread> thread) { | 46 | void Mutex::Acquire(Thread* thread) { |
| 67 | ASSERT_MSG(!ShouldWait(), "object unavailable!"); | 47 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); |
| 68 | 48 | ||
| 69 | // Actually "acquire" the mutex only if we don't already have it... | 49 | // Actually "acquire" the mutex only if we don't already have it |
| 70 | if (lock_count == 0) { | 50 | if (lock_count == 0) { |
| 51 | priority = thread->current_priority; | ||
| 71 | thread->held_mutexes.insert(this); | 52 | thread->held_mutexes.insert(this); |
| 72 | holding_thread = std::move(thread); | 53 | holding_thread = thread; |
| 54 | thread->UpdatePriority(); | ||
| 55 | Core::System::GetInstance().PrepareReschedule(); | ||
| 73 | } | 56 | } |
| 74 | 57 | ||
| 75 | lock_count++; | 58 | lock_count++; |
| 76 | } | 59 | } |
| 77 | 60 | ||
| 78 | void Mutex::Release() { | 61 | void Mutex::Release() { |
| 79 | // Only release if the mutex is held... | 62 | // Only release if the mutex is held |
| 80 | if (lock_count > 0) { | 63 | if (lock_count > 0) { |
| 81 | lock_count--; | 64 | lock_count--; |
| 82 | 65 | ||
| 83 | // 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 |
| 84 | if (lock_count == 0) { | 67 | if (lock_count == 0) { |
| 85 | holding_thread->held_mutexes.erase(this); | 68 | holding_thread->held_mutexes.erase(this); |
| 86 | ResumeWaitingThread(this); | 69 | holding_thread->UpdatePriority(); |
| 70 | holding_thread = nullptr; | ||
| 71 | WakeupAllWaitingThreads(); | ||
| 72 | Core::System::GetInstance().PrepareReschedule(); | ||
| 87 | } | 73 | } |
| 88 | } | 74 | } |
| 89 | } | 75 | } |
| 90 | 76 | ||
| 77 | void Mutex::AddWaitingThread(SharedPtr<Thread> thread) { | ||
| 78 | WaitObject::AddWaitingThread(thread); | ||
| 79 | thread->pending_mutexes.insert(this); | ||
| 80 | UpdatePriority(); | ||
| 81 | } | ||
| 82 | |||
| 83 | void Mutex::RemoveWaitingThread(Thread* thread) { | ||
| 84 | WaitObject::RemoveWaitingThread(thread); | ||
| 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 | } | ||
| 103 | } | ||
| 104 | |||
| 91 | } // namespace | 105 | } // namespace |