summaryrefslogtreecommitdiff
path: root/src/core/hle/kernel/mutex.cpp
diff options
context:
space:
mode:
authorGravatar Subv2017-01-01 16:59:30 -0500
committerGravatar Subv2017-01-04 15:58:46 -0500
commit7abf1853907fe086753df0031262b668a2da88b0 (patch)
tree033c38e1d98f209c32c1378419468212729877b4 /src/core/hle/kernel/mutex.cpp
parentKernel: Object ShouldWait and Acquire calls now take a thread as a parameter. (diff)
downloadyuzu-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/mutex.cpp')
-rw-r--r--src/core/hle/kernel/mutex.cpp58
1 files changed, 45 insertions, 13 deletions
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 @@
13namespace Kernel { 14namespace 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 */
19static void ResumeWaitingThread(Mutex* mutex) { 20static 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
26void ReleaseThreadMutexes(Thread* thread) { 31void 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 {
54void Mutex::Acquire(Thread* thread) { 61void 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
66void Mutex::Release() { 78void 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
94void 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