summaryrefslogtreecommitdiff
path: root/src/core/hle/kernel/mutex.cpp
diff options
context:
space:
mode:
authorGravatar bunnei2018-04-23 16:33:00 -0400
committerGravatar GitHub2018-04-23 16:33:00 -0400
commit0214351f4f0e9377792f8ceb657e3a47aba334d1 (patch)
treef772d4dbaf3d804497740c6c67d1110f20fd010f /src/core/hle/kernel/mutex.cpp
parentMerge pull request #384 from Subv/nvhost-remap (diff)
parentKernel: Implemented mutex priority inheritance. (diff)
downloadyuzu-0214351f4f0e9377792f8ceb657e3a47aba334d1.tar.gz
yuzu-0214351f4f0e9377792f8ceb657e3a47aba334d1.tar.xz
yuzu-0214351f4f0e9377792f8ceb657e3a47aba334d1.zip
Merge pull request #370 from Subv/sync_primitives
Kernel: Reworked the new kernel synchronization primitives.
Diffstat (limited to 'src/core/hle/kernel/mutex.cpp')
-rw-r--r--src/core/hle/kernel/mutex.cpp179
1 files changed, 88 insertions, 91 deletions
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index 0b9dc700c..63733ad79 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -7,6 +7,7 @@
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/core.h"
10#include "core/hle/kernel/errors.h"
10#include "core/hle/kernel/handle_table.h" 11#include "core/hle/kernel/handle_table.h"
11#include "core/hle/kernel/kernel.h" 12#include "core/hle/kernel/kernel.h"
12#include "core/hle/kernel/mutex.h" 13#include "core/hle/kernel/mutex.h"
@@ -15,124 +16,120 @@
15 16
16namespace Kernel { 17namespace Kernel {
17 18
18void ReleaseThreadMutexes(Thread* thread) { 19/// Returns the number of threads that are waiting for a mutex, and the highest priority one among
19 for (auto& mtx : thread->held_mutexes) { 20/// those.
20 mtx->SetHasWaiters(false); 21static std::pair<SharedPtr<Thread>, u32> GetHighestPriorityMutexWaitingThread(
21 mtx->SetHoldingThread(nullptr); 22 SharedPtr<Thread> current_thread, VAddr mutex_addr) {
22 mtx->WakeupAllWaitingThreads();
23 }
24 thread->held_mutexes.clear();
25}
26 23
27Mutex::Mutex() {} 24 SharedPtr<Thread> highest_priority_thread;
28Mutex::~Mutex() {} 25 u32 num_waiters = 0;
29 26
30SharedPtr<Mutex> Mutex::Create(SharedPtr<Kernel::Thread> holding_thread, VAddr guest_addr, 27 for (auto& thread : current_thread->wait_mutex_threads) {
31 std::string name) { 28 if (thread->mutex_wait_address != mutex_addr)
32 SharedPtr<Mutex> mutex(new Mutex); 29 continue;
33 30
34 mutex->guest_addr = guest_addr; 31 ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX);
35 mutex->name = std::move(name);
36 32
37 // If mutex was initialized with a holding thread, acquire it by the holding thread 33 ++num_waiters;
38 if (holding_thread) { 34 if (highest_priority_thread == nullptr ||
39 mutex->Acquire(holding_thread.get()); 35 thread->GetPriority() < highest_priority_thread->GetPriority()) {
36 highest_priority_thread = thread;
37 }
40 } 38 }
41 39
42 // Mutexes are referenced by guest address, so track this in the kernel 40 return {highest_priority_thread, num_waiters};
43 g_object_address_table.Insert(guest_addr, mutex);
44
45 return mutex;
46} 41}
47 42
48bool Mutex::ShouldWait(Thread* thread) const { 43/// Update the mutex owner field of all threads waiting on the mutex to point to the new owner.
49 auto holding_thread = GetHoldingThread(); 44static void TransferMutexOwnership(VAddr mutex_addr, SharedPtr<Thread> current_thread,
50 return holding_thread != nullptr && thread != holding_thread; 45 SharedPtr<Thread> new_owner) {
46 auto threads = current_thread->wait_mutex_threads;
47 for (auto& thread : threads) {
48 if (thread->mutex_wait_address != mutex_addr)
49 continue;
50
51 ASSERT(thread->lock_owner == current_thread);
52 current_thread->RemoveMutexWaiter(thread);
53 if (new_owner != thread)
54 new_owner->AddMutexWaiter(thread);
55 }
51} 56}
52 57
53void Mutex::Acquire(Thread* thread) { 58ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
54 ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); 59 Handle requesting_thread_handle) {
60 // The mutex address must be 4-byte aligned
61 if ((address % sizeof(u32)) != 0) {
62 return ResultCode(ErrorModule::Kernel, ErrCodes::MisalignedAddress);
63 }
55 64
56 priority = thread->current_priority; 65 SharedPtr<Thread> holding_thread = g_handle_table.Get<Thread>(holding_thread_handle);
57 thread->held_mutexes.insert(this); 66 SharedPtr<Thread> requesting_thread = g_handle_table.Get<Thread>(requesting_thread_handle);
58 SetHoldingThread(thread);
59 thread->UpdatePriority();
60 Core::System::GetInstance().PrepareReschedule();
61}
62 67
63ResultCode Mutex::Release(Thread* thread) { 68 // TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of another
64 auto holding_thread = GetHoldingThread(); 69 // thread.
65 ASSERT(holding_thread); 70 ASSERT(requesting_thread == GetCurrentThread());
66 71
67 // We can only release the mutex if it's held by the calling thread. 72 u32 addr_value = Memory::Read32(address);
68 ASSERT(thread == holding_thread); 73
74 // If the mutex isn't being held, just return success.
75 if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) {
76 return RESULT_SUCCESS;
77 }
78
79 if (holding_thread == nullptr)
80 return ERR_INVALID_HANDLE;
81
82 // Wait until the mutex is released
83 GetCurrentThread()->mutex_wait_address = address;
84 GetCurrentThread()->wait_handle = requesting_thread_handle;
85
86 GetCurrentThread()->status = THREADSTATUS_WAIT_MUTEX;
87 GetCurrentThread()->wakeup_callback = nullptr;
88
89 // Update the lock holder thread's priority to prevent priority inversion.
90 holding_thread->AddMutexWaiter(GetCurrentThread());
69 91
70 holding_thread->held_mutexes.erase(this);
71 holding_thread->UpdatePriority();
72 SetHoldingThread(nullptr);
73 SetHasWaiters(!GetWaitingThreads().empty());
74 WakeupAllWaitingThreads();
75 Core::System::GetInstance().PrepareReschedule(); 92 Core::System::GetInstance().PrepareReschedule();
76 93
77 return RESULT_SUCCESS; 94 return RESULT_SUCCESS;
78} 95}
79 96
80void Mutex::AddWaitingThread(SharedPtr<Thread> thread) { 97ResultCode Mutex::Release(VAddr address) {
81 WaitObject::AddWaitingThread(thread); 98 // The mutex address must be 4-byte aligned
82 thread->pending_mutexes.insert(this); 99 if ((address % sizeof(u32)) != 0) {
83 SetHasWaiters(true); 100 return ResultCode(ErrorModule::Kernel, ErrCodes::MisalignedAddress);
84 UpdatePriority(); 101 }
85}
86
87void Mutex::RemoveWaitingThread(Thread* thread) {
88 WaitObject::RemoveWaitingThread(thread);
89 thread->pending_mutexes.erase(this);
90 if (!GetHasWaiters())
91 SetHasWaiters(!GetWaitingThreads().empty());
92 UpdatePriority();
93}
94 102
95void Mutex::UpdatePriority() { 103 auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(GetCurrentThread(), address);
96 if (!GetHoldingThread())
97 return;
98 104
99 u32 best_priority = THREADPRIO_LOWEST; 105 // There are no more threads waiting for the mutex, release it completely.
100 for (auto& waiter : GetWaitingThreads()) { 106 if (thread == nullptr) {
101 if (waiter->current_priority < best_priority) 107 ASSERT(GetCurrentThread()->wait_mutex_threads.empty());
102 best_priority = waiter->current_priority; 108 Memory::Write32(address, 0);
109 return RESULT_SUCCESS;
103 } 110 }
104 111
105 if (best_priority != priority) { 112 // Transfer the ownership of the mutex from the previous owner to the new one.
106 priority = best_priority; 113 TransferMutexOwnership(address, GetCurrentThread(), thread);
107 GetHoldingThread()->UpdatePriority();
108 }
109}
110 114
111Handle Mutex::GetOwnerHandle() const { 115 u32 mutex_value = thread->wait_handle;
112 GuestState guest_state{Memory::Read32(guest_addr)};
113 return guest_state.holding_thread_handle;
114}
115 116
116SharedPtr<Thread> Mutex::GetHoldingThread() const { 117 if (num_waiters >= 2) {
117 GuestState guest_state{Memory::Read32(guest_addr)}; 118 // Notify the guest that there are still some threads waiting for the mutex
118 return g_handle_table.Get<Thread>(guest_state.holding_thread_handle); 119 mutex_value |= Mutex::MutexHasWaitersFlag;
119} 120 }
120 121
121void Mutex::SetHoldingThread(SharedPtr<Thread> thread) { 122 // Grant the mutex to the next waiting thread and resume it.
122 GuestState guest_state{Memory::Read32(guest_addr)}; 123 Memory::Write32(address, mutex_value);
123 guest_state.holding_thread_handle.Assign(thread ? thread->guest_handle : 0);
124 Memory::Write32(guest_addr, guest_state.raw);
125}
126 124
127bool Mutex::GetHasWaiters() const { 125 ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX);
128 GuestState guest_state{Memory::Read32(guest_addr)}; 126 thread->ResumeFromWait();
129 return guest_state.has_waiters != 0;
130}
131 127
132void Mutex::SetHasWaiters(bool has_waiters) { 128 thread->lock_owner = nullptr;
133 GuestState guest_state{Memory::Read32(guest_addr)}; 129 thread->condvar_wait_address = 0;
134 guest_state.has_waiters.Assign(has_waiters ? 1 : 0); 130 thread->mutex_wait_address = 0;
135 Memory::Write32(guest_addr, guest_state.raw); 131 thread->wait_handle = 0;
136}
137 132
133 return RESULT_SUCCESS;
134}
138} // namespace Kernel 135} // namespace Kernel