summaryrefslogtreecommitdiff
path: root/src/core/hle
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
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')
-rw-r--r--src/core/hle/kernel/kernel.h2
-rw-r--r--src/core/hle/kernel/mutex.cpp58
-rw-r--r--src/core/hle/kernel/mutex.h7
-rw-r--r--src/core/hle/kernel/thread.cpp6
-rw-r--r--src/core/hle/svc.cpp9
5 files changed, 51 insertions, 31 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 @@
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
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
52private: 49private:
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
92void Thread::Stop() { 92void 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);
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index 159ac0bf6..5d6359344 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -278,9 +278,6 @@ static ResultCode WaitSynchronization1(Kernel::Handle handle, s64 nano_seconds)
278 return ERR_SYNC_TIMEOUT; 278 return ERR_SYNC_TIMEOUT;
279 279
280 object->AddWaitingThread(thread); 280 object->AddWaitingThread(thread);
281 // TODO(Subv): Perform things like update the mutex lock owner's priority to
282 // prevent priority inversion. Currently this is done in Mutex::ShouldWait,
283 // but it should be moved to a function that is called from here.
284 thread->status = THREADSTATUS_WAIT_SYNCH; 281 thread->status = THREADSTATUS_WAIT_SYNCH;
285 282
286 // Create an event to wake the thread up after the specified nanosecond delay has passed 283 // Create an event to wake the thread up after the specified nanosecond delay has passed
@@ -359,9 +356,6 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha
359 // Add the thread to each of the objects' waiting threads. 356 // Add the thread to each of the objects' waiting threads.
360 for (auto& object : objects) { 357 for (auto& object : objects) {
361 object->AddWaitingThread(thread); 358 object->AddWaitingThread(thread);
362 // TODO(Subv): Perform things like update the mutex lock owner's priority to
363 // prevent priority inversion. Currently this is done in Mutex::ShouldWait,
364 // but it should be moved to a function that is called from here.
365 } 359 }
366 360
367 // Set the thread's waitlist to the list of objects passed to WaitSynchronizationN 361 // Set the thread's waitlist to the list of objects passed to WaitSynchronizationN
@@ -409,9 +403,6 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha
409 // Set the index of this object in the mapping of Objects -> index for this thread. 403 // Set the index of this object in the mapping of Objects -> index for this thread.
410 thread->wait_objects_index[object->GetObjectId()] = static_cast<int>(i); 404 thread->wait_objects_index[object->GetObjectId()] = static_cast<int>(i);
411 object->AddWaitingThread(thread); 405 object->AddWaitingThread(thread);
412 // TODO(Subv): Perform things like update the mutex lock owner's priority to
413 // prevent priority inversion. Currently this is done in Mutex::ShouldWait,
414 // but it should be moved to a function that is called from here.
415 } 406 }
416 407
417 // Note: If no handles and no timeout were given, then the thread will deadlock, this is 408 // Note: If no handles and no timeout were given, then the thread will deadlock, this is