summaryrefslogtreecommitdiff
path: root/src
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
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')
-rw-r--r--src/core/hle/kernel/errors.h1
-rw-r--r--src/core/hle/kernel/mutex.cpp94
-rw-r--r--src/core/hle/kernel/mutex.h12
-rw-r--r--src/core/hle/kernel/svc.cpp22
-rw-r--r--src/core/hle/kernel/thread.cpp13
-rw-r--r--src/core/hle/kernel/thread.h6
6 files changed, 126 insertions, 22 deletions
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h
index 29d8dfdaa..5be20c878 100644
--- a/src/core/hle/kernel/errors.h
+++ b/src/core/hle/kernel/errors.h
@@ -20,6 +20,7 @@ enum {
20 MaxConnectionsReached = 52, 20 MaxConnectionsReached = 52,
21 21
22 // Confirmed Switch OS error codes 22 // Confirmed Switch OS error codes
23 MisalignedAddress = 102,
23 InvalidHandle = 114, 24 InvalidHandle = 114,
24 Timeout = 117, 25 Timeout = 117,
25 SynchronizationCanceled = 118, 26 SynchronizationCanceled = 118,
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
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h
index 38db21005..310923087 100644
--- a/src/core/hle/kernel/mutex.h
+++ b/src/core/hle/kernel/mutex.h
@@ -77,6 +77,18 @@ public:
77 /// Sets the has_waiters bit in the guest state. 77 /// Sets the has_waiters bit in the guest state.
78 void SetHasWaiters(bool has_waiters); 78 void SetHasWaiters(bool has_waiters);
79 79
80 /// Flag that indicates that a mutex still has threads waiting for it.
81 static constexpr u32 MutexHasWaitersFlag = 0x40000000;
82 /// Mask of the bits in a mutex address value that contain the mutex owner.
83 static constexpr u32 MutexOwnerMask = 0xBFFFFFFF;
84
85 /// Attempts to acquire a mutex at the specified address.
86 static ResultCode TryAcquire(VAddr address, Handle holding_thread_handle,
87 Handle requesting_thread_handle);
88
89 /// Releases the mutex at the specified address.
90 static ResultCode Release(VAddr address);
91
80private: 92private:
81 Mutex(); 93 Mutex();
82 ~Mutex() override; 94 ~Mutex() override;
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 6204bcaaa..92273b488 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -262,32 +262,14 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr,
262 "requesting_current_thread_handle=0x%08X", 262 "requesting_current_thread_handle=0x%08X",
263 holding_thread_handle, mutex_addr, requesting_thread_handle); 263 holding_thread_handle, mutex_addr, requesting_thread_handle);
264 264
265 SharedPtr<Thread> holding_thread = g_handle_table.Get<Thread>(holding_thread_handle); 265 return Mutex::TryAcquire(mutex_addr, holding_thread_handle, requesting_thread_handle);
266 SharedPtr<Thread> requesting_thread = g_handle_table.Get<Thread>(requesting_thread_handle);
267
268 ASSERT(requesting_thread);
269 ASSERT(requesting_thread == GetCurrentThread());
270
271 SharedPtr<Mutex> mutex = g_object_address_table.Get<Mutex>(mutex_addr);
272 if (!mutex) {
273 // Create a new mutex for the specified address if one does not already exist
274 mutex = Mutex::Create(holding_thread, mutex_addr);
275 mutex->name = Common::StringFromFormat("mutex-%llx", mutex_addr);
276 }
277
278 ASSERT(holding_thread == mutex->GetHoldingThread());
279
280 return WaitSynchronization1(mutex, requesting_thread.get());
281} 266}
282 267
283/// Unlock a mutex 268/// Unlock a mutex
284static ResultCode ArbitrateUnlock(VAddr mutex_addr) { 269static ResultCode ArbitrateUnlock(VAddr mutex_addr) {
285 LOG_TRACE(Kernel_SVC, "called mutex_addr=0x%llx", mutex_addr); 270 LOG_TRACE(Kernel_SVC, "called mutex_addr=0x%llx", mutex_addr);
286 271
287 SharedPtr<Mutex> mutex = g_object_address_table.Get<Mutex>(mutex_addr); 272 return Mutex::Release(mutex_addr);
288 ASSERT(mutex);
289
290 return mutex->Release(GetCurrentThread());
291} 273}
292 274
293/// Break program execution 275/// Break program execution
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index f3a8aa4aa..0a0ad7cfb 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -126,6 +126,14 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
126 resume = thread->wakeup_callback(ThreadWakeupReason::Timeout, thread, nullptr, 0); 126 resume = thread->wakeup_callback(ThreadWakeupReason::Timeout, thread, nullptr, 0);
127 } 127 }
128 128
129 if (thread->mutex_wait_address != 0 || thread->condvar_wait_address != 0 ||
130 thread->wait_handle) {
131 ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX);
132 thread->mutex_wait_address = 0;
133 thread->condvar_wait_address = 0;
134 thread->wait_handle = 0;
135 }
136
129 if (resume) 137 if (resume)
130 thread->ResumeFromWait(); 138 thread->ResumeFromWait();
131} 139}
@@ -151,6 +159,7 @@ void Thread::ResumeFromWait() {
151 case THREADSTATUS_WAIT_HLE_EVENT: 159 case THREADSTATUS_WAIT_HLE_EVENT:
152 case THREADSTATUS_WAIT_SLEEP: 160 case THREADSTATUS_WAIT_SLEEP:
153 case THREADSTATUS_WAIT_IPC: 161 case THREADSTATUS_WAIT_IPC:
162 case THREADSTATUS_WAIT_MUTEX:
154 break; 163 break;
155 164
156 case THREADSTATUS_READY: 165 case THREADSTATUS_READY:
@@ -256,7 +265,9 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
256 thread->last_running_ticks = CoreTiming::GetTicks(); 265 thread->last_running_ticks = CoreTiming::GetTicks();
257 thread->processor_id = processor_id; 266 thread->processor_id = processor_id;
258 thread->wait_objects.clear(); 267 thread->wait_objects.clear();
259 thread->wait_address = 0; 268 thread->mutex_wait_address = 0;
269 thread->condvar_wait_address = 0;
270 thread->wait_handle = 0;
260 thread->name = std::move(name); 271 thread->name = std::move(name);
261 thread->callback_handle = wakeup_callback_handle_table.Create(thread).Unwrap(); 272 thread->callback_handle = wakeup_callback_handle_table.Create(thread).Unwrap();
262 thread->owner_process = owner_process; 273 thread->owner_process = owner_process;
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index dbf47e269..a3a6e6a64 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -43,6 +43,7 @@ enum ThreadStatus {
43 THREADSTATUS_WAIT_IPC, ///< Waiting for the reply from an IPC request 43 THREADSTATUS_WAIT_IPC, ///< Waiting for the reply from an IPC request
44 THREADSTATUS_WAIT_SYNCH_ANY, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false 44 THREADSTATUS_WAIT_SYNCH_ANY, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false
45 THREADSTATUS_WAIT_SYNCH_ALL, ///< Waiting due to WaitSynchronizationN with wait_all = true 45 THREADSTATUS_WAIT_SYNCH_ALL, ///< Waiting due to WaitSynchronizationN with wait_all = true
46 THREADSTATUS_WAIT_MUTEX, ///< Waiting due to an ArbitrateLock/WaitProcessWideKey svc
46 THREADSTATUS_DORMANT, ///< Created but not yet made ready 47 THREADSTATUS_DORMANT, ///< Created but not yet made ready
47 THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated 48 THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated
48}; 49};
@@ -217,7 +218,10 @@ public:
217 // passed to WaitSynchronization1/N. 218 // passed to WaitSynchronization1/N.
218 std::vector<SharedPtr<WaitObject>> wait_objects; 219 std::vector<SharedPtr<WaitObject>> wait_objects;
219 220
220 VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address 221 // If waiting on a ConditionVariable, this is the ConditionVariable address
222 VAddr condvar_wait_address;
223 VAddr mutex_wait_address; ///< If waiting on a Mutex, this is the mutex address
224 Handle wait_handle; ///< The handle used to wait for the mutex.
221 225
222 std::string name; 226 std::string name;
223 227