diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/hle/kernel/errors.h | 1 | ||||
| -rw-r--r-- | src/core/hle/kernel/mutex.cpp | 94 | ||||
| -rw-r--r-- | src/core/hle/kernel/mutex.h | 12 | ||||
| -rw-r--r-- | src/core/hle/kernel/svc.cpp | 22 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.cpp | 13 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.h | 6 |
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 | ||
| 16 | namespace Kernel { | 17 | namespace Kernel { |
| 17 | 18 | ||
| 19 | /// Returns the number of threads that are waiting for a mutex, and the highest priority one among | ||
| 20 | /// those. | ||
| 21 | static 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 | |||
| 18 | void ReleaseThreadMutexes(Thread* thread) { | 43 | void 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 | ||
| 163 | ResultCode 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 | |||
| 199 | ResultCode 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 | |||
| 80 | private: | 92 | private: |
| 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 |
| 284 | static ResultCode ArbitrateUnlock(VAddr mutex_addr) { | 269 | static 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 | ||