summaryrefslogtreecommitdiff
path: root/src/core/hle/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle/kernel')
-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
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 @@
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);