summaryrefslogtreecommitdiff
path: root/src/core/hle/kernel/mutex.cpp
diff options
context:
space:
mode:
authorGravatar Subv2018-04-20 12:01:14 -0500
committerGravatar Subv2018-04-20 21:04:25 -0500
commite81a2080ebf9712231dd29c081141780ffd46cfb (patch)
treeb93d23dde3c3a0e86edeb44a1c1f1e18ac839bd6 /src/core/hle/kernel/mutex.cpp
parentMerge pull request #367 from lioncash/clamp (diff)
downloadyuzu-e81a2080ebf9712231dd29c081141780ffd46cfb.tar.gz
yuzu-e81a2080ebf9712231dd29c081141780ffd46cfb.tar.xz
yuzu-e81a2080ebf9712231dd29c081141780ffd46cfb.zip
Kernel: Corrected the implementation of svcArbitrateLock and svcArbitrateUnlock.
Switch mutexes are no longer kernel objects, they are managed in userland and only use the kernel to handle the contention case. Mutex addresses store a special flag value (0x40000000) to notify the guest code that there are still some threads waiting for the mutex to be released. This flag is updated when a thread calls ArbitrateUnlock. TODO: * Fix svcWaitProcessWideKey * Fix svcSignalProcessWideKey * Remove the Mutex class.
Diffstat (limited to 'src/core/hle/kernel/mutex.cpp')
-rw-r--r--src/core/hle/kernel/mutex.cpp94
1 files changed, 94 insertions, 0 deletions
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index 0b9dc700c..50a9a0805 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,6 +16,30 @@
15 16
16namespace Kernel { 17namespace Kernel {
17 18
19/// Returns the number of threads that are waiting for a mutex, and the highest priority one among
20/// those.
21static std::pair<SharedPtr<Thread>, u32> GetHighestPriorityMutexWaitingThread(VAddr mutex_addr) {
22 auto& thread_list = Core::System::GetInstance().Scheduler().GetThreadList();
23
24 SharedPtr<Thread> highest_priority_thread;
25 u32 num_waiters = 0;
26
27 for (auto& thread : thread_list) {
28 if (thread->mutex_wait_address != mutex_addr)
29 continue;
30
31 ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX);
32
33 ++num_waiters;
34 if (highest_priority_thread == nullptr ||
35 thread->GetPriority() < highest_priority_thread->GetPriority()) {
36 highest_priority_thread = thread;
37 }
38 }
39
40 return {highest_priority_thread, num_waiters};
41}
42
18void ReleaseThreadMutexes(Thread* thread) { 43void ReleaseThreadMutexes(Thread* thread) {
19 for (auto& mtx : thread->held_mutexes) { 44 for (auto& mtx : thread->held_mutexes) {
20 mtx->SetHasWaiters(false); 45 mtx->SetHasWaiters(false);
@@ -135,4 +160,73 @@ void Mutex::SetHasWaiters(bool has_waiters) {
135 Memory::Write32(guest_addr, guest_state.raw); 160 Memory::Write32(guest_addr, guest_state.raw);
136} 161}
137 162
163ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
164 Handle requesting_thread_handle) {
165 // The mutex address must be 4-byte aligned
166 if ((address % sizeof(u32)) != 0) {
167 return ResultCode(ErrorModule::Kernel, ErrCodes::MisalignedAddress);
168 }
169
170 SharedPtr<Thread> holding_thread = g_handle_table.Get<Thread>(holding_thread_handle);
171 SharedPtr<Thread> requesting_thread = g_handle_table.Get<Thread>(requesting_thread_handle);
172
173 // TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of another
174 // thread.
175 ASSERT(requesting_thread == GetCurrentThread());
176
177 u32 addr_value = Memory::Read32(address);
178
179 // If the mutex isn't being held, just return success.
180 if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) {
181 return RESULT_SUCCESS;
182 }
183
184 if (holding_thread == nullptr)
185 return ERR_INVALID_HANDLE;
186
187 // Wait until the mutex is released
188 requesting_thread->mutex_wait_address = address;
189 requesting_thread->wait_handle = requesting_thread_handle;
190
191 requesting_thread->status = THREADSTATUS_WAIT_MUTEX;
192 requesting_thread->wakeup_callback = nullptr;
193
194 Core::System::GetInstance().PrepareReschedule();
195
196 return RESULT_SUCCESS;
197}
198
199ResultCode Mutex::Release(VAddr address) {
200 // The mutex address must be 4-byte aligned
201 if ((address % sizeof(u32)) != 0) {
202 return ResultCode(ErrorModule::Kernel, ErrCodes::MisalignedAddress);
203 }
204
205 auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(address);
206
207 // There are no more threads waiting for the mutex, release it completely.
208 if (thread == nullptr) {
209 Memory::Write32(address, 0);
210 return RESULT_SUCCESS;
211 }
212
213 u32 mutex_value = thread->wait_handle;
214
215 if (num_waiters >= 2) {
216 // Notify the guest that there are still some threads waiting for the mutex
217 mutex_value |= Mutex::MutexHasWaitersFlag;
218 }
219
220 // Grant the mutex to the next waiting thread and resume it.
221 Memory::Write32(address, mutex_value);
222
223 ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX);
224 thread->ResumeFromWait();
225
226 thread->condvar_wait_address = 0;
227 thread->mutex_wait_address = 0;
228 thread->wait_handle = 0;
229
230 return RESULT_SUCCESS;
231}
138} // namespace Kernel 232} // namespace Kernel