diff options
| author | 2015-01-21 21:09:47 -0500 | |
|---|---|---|
| committer | 2015-01-21 21:09:47 -0500 | |
| commit | 24a63662ba6c7816001bba399e85d8c131a89489 (patch) | |
| tree | a9959e69723b4f19550834171c962ec06c9e34b7 /src/core/hle/kernel/thread.cpp | |
| parent | Merge pull request #491 from archshift/hidspvr (diff) | |
| parent | WaitSynchronization: Added a result code for invalid result, fixed bug. (diff) | |
| download | yuzu-24a63662ba6c7816001bba399e85d8c131a89489.tar.gz yuzu-24a63662ba6c7816001bba399e85d8c131a89489.tar.xz yuzu-24a63662ba6c7816001bba399e85d8c131a89489.zip | |
Merge pull request #495 from bunnei/fix-waitsynch
Fix WaitSynchronization
Diffstat (limited to 'src/core/hle/kernel/thread.cpp')
| -rw-r--r-- | src/core/hle/kernel/thread.cpp | 172 |
1 files changed, 113 insertions, 59 deletions
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index bc86a7c59..03b492c75 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -22,17 +22,12 @@ | |||
| 22 | 22 | ||
| 23 | namespace Kernel { | 23 | namespace Kernel { |
| 24 | 24 | ||
| 25 | ResultVal<bool> Thread::WaitSynchronization() { | 25 | bool Thread::ShouldWait() { |
| 26 | const bool wait = status != THREADSTATUS_DORMANT; | 26 | return status != THREADSTATUS_DORMANT; |
| 27 | if (wait) { | 27 | } |
| 28 | Thread* thread = GetCurrentThread(); | ||
| 29 | if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) { | ||
| 30 | waiting_threads.push_back(thread); | ||
| 31 | } | ||
| 32 | WaitCurrentThread(WAITTYPE_THREADEND, this); | ||
| 33 | } | ||
| 34 | 28 | ||
| 35 | return MakeResult<bool>(wait); | 29 | void Thread::Acquire() { |
| 30 | _assert_msg_(Kernel, !ShouldWait(), "object unavailable!"); | ||
| 36 | } | 31 | } |
| 37 | 32 | ||
| 38 | // Lists all thread ids that aren't deleted/etc. | 33 | // Lists all thread ids that aren't deleted/etc. |
| @@ -67,8 +62,8 @@ static void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { | |||
| 67 | if (t->current_priority < lowest_priority) { | 62 | if (t->current_priority < lowest_priority) { |
| 68 | t->current_priority = t->initial_priority; | 63 | t->current_priority = t->initial_priority; |
| 69 | } | 64 | } |
| 70 | t->wait_type = WAITTYPE_NONE; | 65 | |
| 71 | t->wait_object = nullptr; | 66 | t->wait_objects.clear(); |
| 72 | t->wait_address = 0; | 67 | t->wait_address = 0; |
| 73 | } | 68 | } |
| 74 | 69 | ||
| @@ -88,37 +83,32 @@ static void ChangeReadyState(Thread* t, bool ready) { | |||
| 88 | } | 83 | } |
| 89 | } | 84 | } |
| 90 | 85 | ||
| 91 | /// Check if a thread is blocking on a specified wait type | 86 | /// Check if a thread is waiting on a the specified wait object |
| 92 | static bool CheckWaitType(const Thread* thread, WaitType type) { | 87 | static bool CheckWait_WaitObject(const Thread* thread, WaitObject* wait_object) { |
| 93 | return (type == thread->wait_type) && (thread->IsWaiting()); | 88 | auto itr = std::find(thread->wait_objects.begin(), thread->wait_objects.end(), wait_object); |
| 94 | } | 89 | |
| 90 | if (itr != thread->wait_objects.end()) | ||
| 91 | return thread->IsWaiting(); | ||
| 95 | 92 | ||
| 96 | /// Check if a thread is blocking on a specified wait type with a specified handle | 93 | return false; |
| 97 | static bool CheckWaitType(const Thread* thread, WaitType type, Object* wait_object) { | ||
| 98 | return CheckWaitType(thread, type) && wait_object == thread->wait_object; | ||
| 99 | } | 94 | } |
| 100 | 95 | ||
| 101 | /// Check if a thread is blocking on a specified wait type with a specified handle and address | 96 | /// Check if the specified thread is waiting on the specified address to be arbitrated |
| 102 | static bool CheckWaitType(const Thread* thread, WaitType type, Object* wait_object, VAddr wait_address) { | 97 | static bool CheckWait_AddressArbiter(const Thread* thread, VAddr wait_address) { |
| 103 | return CheckWaitType(thread, type, wait_object) && (wait_address == thread->wait_address); | 98 | return thread->IsWaiting() && thread->wait_objects.empty() && wait_address == thread->wait_address; |
| 104 | } | 99 | } |
| 105 | 100 | ||
| 106 | /// Stops the current thread | 101 | /// Stops the current thread |
| 107 | void Thread::Stop(const char* reason) { | 102 | void Thread::Stop(const char* reason) { |
| 108 | // Release all the mutexes that this thread holds | 103 | // Release all the mutexes that this thread holds |
| 109 | ReleaseThreadMutexes(GetHandle()); | 104 | ReleaseThreadMutexes(this); |
| 110 | 105 | ||
| 111 | ChangeReadyState(this, false); | 106 | ChangeReadyState(this, false); |
| 112 | status = THREADSTATUS_DORMANT; | 107 | status = THREADSTATUS_DORMANT; |
| 113 | for (auto& waiting_thread : waiting_threads) { | 108 | WakeupAllWaitingThreads(); |
| 114 | if (CheckWaitType(waiting_thread.get(), WAITTYPE_THREADEND, this)) | ||
| 115 | waiting_thread->ResumeFromWait(); | ||
| 116 | } | ||
| 117 | waiting_threads.clear(); | ||
| 118 | 109 | ||
| 119 | // Stopped threads are never waiting. | 110 | // Stopped threads are never waiting. |
| 120 | wait_type = WAITTYPE_NONE; | 111 | wait_objects.clear(); |
| 121 | wait_object = nullptr; | ||
| 122 | wait_address = 0; | 112 | wait_address = 0; |
| 123 | } | 113 | } |
| 124 | 114 | ||
| @@ -129,26 +119,20 @@ static void ChangeThreadState(Thread* t, ThreadStatus new_status) { | |||
| 129 | } | 119 | } |
| 130 | ChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0); | 120 | ChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0); |
| 131 | t->status = new_status; | 121 | t->status = new_status; |
| 132 | |||
| 133 | if (new_status == THREADSTATUS_WAIT) { | ||
| 134 | if (t->wait_type == WAITTYPE_NONE) { | ||
| 135 | LOG_ERROR(Kernel, "Waittype none not allowed"); | ||
| 136 | } | ||
| 137 | } | ||
| 138 | } | 122 | } |
| 139 | 123 | ||
| 140 | /// Arbitrate the highest priority thread that is waiting | 124 | /// Arbitrate the highest priority thread that is waiting |
| 141 | Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address) { | 125 | Thread* ArbitrateHighestPriorityThread(u32 address) { |
| 142 | Thread* highest_priority_thread = nullptr; | 126 | Thread* highest_priority_thread = nullptr; |
| 143 | s32 priority = THREADPRIO_LOWEST; | 127 | s32 priority = THREADPRIO_LOWEST; |
| 144 | 128 | ||
| 145 | // Iterate through threads, find highest priority thread that is waiting to be arbitrated... | 129 | // Iterate through threads, find highest priority thread that is waiting to be arbitrated... |
| 146 | for (auto& thread : thread_list) { | 130 | for (auto& thread : thread_list) { |
| 147 | if (!CheckWaitType(thread.get(), WAITTYPE_ARB, arbiter, address)) | 131 | if (!CheckWait_AddressArbiter(thread.get(), address)) |
| 148 | continue; | 132 | continue; |
| 149 | 133 | ||
| 150 | if (thread == nullptr) | 134 | if (thread == nullptr) |
| 151 | continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up. | 135 | continue; |
| 152 | 136 | ||
| 153 | if(thread->current_priority <= priority) { | 137 | if(thread->current_priority <= priority) { |
| 154 | highest_priority_thread = thread.get(); | 138 | highest_priority_thread = thread.get(); |
| @@ -165,11 +149,11 @@ Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address) { | |||
| 165 | } | 149 | } |
| 166 | 150 | ||
| 167 | /// Arbitrate all threads currently waiting | 151 | /// Arbitrate all threads currently waiting |
| 168 | void ArbitrateAllThreads(Object* arbiter, u32 address) { | 152 | void ArbitrateAllThreads(u32 address) { |
| 169 | 153 | ||
| 170 | // Iterate through threads, find highest priority thread that is waiting to be arbitrated... | 154 | // Iterate through threads, find highest priority thread that is waiting to be arbitrated... |
| 171 | for (auto& thread : thread_list) { | 155 | for (auto& thread : thread_list) { |
| 172 | if (CheckWaitType(thread.get(), WAITTYPE_ARB, arbiter, address)) | 156 | if (CheckWait_AddressArbiter(thread.get(), address)) |
| 173 | thread->ResumeFromWait(); | 157 | thread->ResumeFromWait(); |
| 174 | } | 158 | } |
| 175 | } | 159 | } |
| @@ -177,9 +161,6 @@ void ArbitrateAllThreads(Object* arbiter, u32 address) { | |||
| 177 | /// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields) | 161 | /// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields) |
| 178 | static void CallThread(Thread* t) { | 162 | static void CallThread(Thread* t) { |
| 179 | // Stop waiting | 163 | // Stop waiting |
| 180 | if (t->wait_type != WAITTYPE_NONE) { | ||
| 181 | t->wait_type = WAITTYPE_NONE; | ||
| 182 | } | ||
| 183 | ChangeThreadState(t, THREADSTATUS_READY); | 164 | ChangeThreadState(t, THREADSTATUS_READY); |
| 184 | } | 165 | } |
| 185 | 166 | ||
| @@ -200,7 +181,6 @@ static void SwitchContext(Thread* t) { | |||
| 200 | current_thread = t; | 181 | current_thread = t; |
| 201 | ChangeReadyState(t, false); | 182 | ChangeReadyState(t, false); |
| 202 | t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; | 183 | t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; |
| 203 | t->wait_type = WAITTYPE_NONE; | ||
| 204 | Core::g_app_core->LoadContext(t->context); | 184 | Core::g_app_core->LoadContext(t->context); |
| 205 | } else { | 185 | } else { |
| 206 | current_thread = nullptr; | 186 | current_thread = nullptr; |
| @@ -223,16 +203,27 @@ static Thread* NextThread() { | |||
| 223 | return next; | 203 | return next; |
| 224 | } | 204 | } |
| 225 | 205 | ||
| 226 | void WaitCurrentThread(WaitType wait_type, Object* wait_object) { | 206 | void WaitCurrentThread_Sleep() { |
| 227 | Thread* thread = GetCurrentThread(); | 207 | Thread* thread = GetCurrentThread(); |
| 228 | thread->wait_type = wait_type; | ||
| 229 | thread->wait_object = wait_object; | ||
| 230 | ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); | 208 | ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); |
| 231 | } | 209 | } |
| 232 | 210 | ||
| 233 | void WaitCurrentThread(WaitType wait_type, Object* wait_object, VAddr wait_address) { | 211 | void WaitCurrentThread_WaitSynchronization(SharedPtr<WaitObject> wait_object, bool wait_set_output, bool wait_all) { |
| 234 | WaitCurrentThread(wait_type, wait_object); | 212 | Thread* thread = GetCurrentThread(); |
| 235 | GetCurrentThread()->wait_address = wait_address; | 213 | thread->wait_set_output = wait_set_output; |
| 214 | thread->wait_all = wait_all; | ||
| 215 | |||
| 216 | // It's possible to call WaitSynchronizationN without any objects passed in... | ||
| 217 | if (wait_object != nullptr) | ||
| 218 | thread->wait_objects.push_back(wait_object); | ||
| 219 | |||
| 220 | ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); | ||
| 221 | } | ||
| 222 | |||
| 223 | void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) { | ||
| 224 | Thread* thread = GetCurrentThread(); | ||
| 225 | thread->wait_address = wait_address; | ||
| 226 | ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); | ||
| 236 | } | 227 | } |
| 237 | 228 | ||
| 238 | /// Event type for the thread wake up event | 229 | /// Event type for the thread wake up event |
| @@ -247,6 +238,12 @@ static void ThreadWakeupCallback(u64 parameter, int cycles_late) { | |||
| 247 | return; | 238 | return; |
| 248 | } | 239 | } |
| 249 | 240 | ||
| 241 | thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS, | ||
| 242 | ErrorSummary::StatusChanged, ErrorLevel::Info)); | ||
| 243 | |||
| 244 | if (thread->wait_set_output) | ||
| 245 | thread->SetWaitSynchronizationOutput(-1); | ||
| 246 | |||
| 250 | thread->ResumeFromWait(); | 247 | thread->ResumeFromWait(); |
| 251 | } | 248 | } |
| 252 | 249 | ||
| @@ -261,14 +258,63 @@ void WakeThreadAfterDelay(Thread* thread, s64 nanoseconds) { | |||
| 261 | CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, thread->GetHandle()); | 258 | CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, thread->GetHandle()); |
| 262 | } | 259 | } |
| 263 | 260 | ||
| 264 | /// Resumes a thread from waiting by marking it as "ready" | 261 | void Thread::ReleaseWaitObject(WaitObject* wait_object) { |
| 262 | if (wait_objects.empty()) { | ||
| 263 | LOG_CRITICAL(Kernel, "thread is not waiting on any objects!"); | ||
| 264 | return; | ||
| 265 | } | ||
| 266 | |||
| 267 | // Remove this thread from the waiting object's thread list | ||
| 268 | wait_object->RemoveWaitingThread(this); | ||
| 269 | |||
| 270 | unsigned index = 0; | ||
| 271 | bool wait_all_failed = false; // Will be set to true if any object is unavailable | ||
| 272 | |||
| 273 | // Iterate through all waiting objects to check availability... | ||
| 274 | for (auto itr = wait_objects.begin(); itr != wait_objects.end(); ++itr) { | ||
| 275 | if ((*itr)->ShouldWait()) | ||
| 276 | wait_all_failed = true; | ||
| 277 | |||
| 278 | // The output should be the last index of wait_object | ||
| 279 | if (*itr == wait_object) | ||
| 280 | index = itr - wait_objects.begin(); | ||
| 281 | } | ||
| 282 | |||
| 283 | // If we are waiting on all objects... | ||
| 284 | if (wait_all) { | ||
| 285 | // Resume the thread only if all are available... | ||
| 286 | if (!wait_all_failed) { | ||
| 287 | SetWaitSynchronizationResult(RESULT_SUCCESS); | ||
| 288 | SetWaitSynchronizationOutput(-1); | ||
| 289 | |||
| 290 | ResumeFromWait(); | ||
| 291 | } | ||
| 292 | } else { | ||
| 293 | // Otherwise, resume | ||
| 294 | SetWaitSynchronizationResult(RESULT_SUCCESS); | ||
| 295 | |||
| 296 | if (wait_set_output) | ||
| 297 | SetWaitSynchronizationOutput(index); | ||
| 298 | |||
| 299 | ResumeFromWait(); | ||
| 300 | } | ||
| 301 | } | ||
| 302 | |||
| 265 | void Thread::ResumeFromWait() { | 303 | void Thread::ResumeFromWait() { |
| 266 | // Cancel any outstanding wakeup events | 304 | // Cancel any outstanding wakeup events |
| 267 | CoreTiming::UnscheduleEvent(ThreadWakeupEventType, GetHandle()); | 305 | CoreTiming::UnscheduleEvent(ThreadWakeupEventType, GetHandle()); |
| 268 | 306 | ||
| 269 | status &= ~THREADSTATUS_WAIT; | 307 | status &= ~THREADSTATUS_WAIT; |
| 270 | wait_object = nullptr; | 308 | |
| 271 | wait_type = WAITTYPE_NONE; | 309 | // Remove this thread from all other WaitObjects |
| 310 | for (auto wait_object : wait_objects) | ||
| 311 | wait_object->RemoveWaitingThread(this); | ||
| 312 | |||
| 313 | wait_objects.clear(); | ||
| 314 | wait_set_output = false; | ||
| 315 | wait_all = false; | ||
| 316 | wait_address = 0; | ||
| 317 | |||
| 272 | if (!(status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { | 318 | if (!(status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { |
| 273 | ChangeReadyState(this, true); | 319 | ChangeReadyState(this, true); |
| 274 | } | 320 | } |
| @@ -334,8 +380,9 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, | |||
| 334 | thread->stack_size = stack_size; | 380 | thread->stack_size = stack_size; |
| 335 | thread->initial_priority = thread->current_priority = priority; | 381 | thread->initial_priority = thread->current_priority = priority; |
| 336 | thread->processor_id = processor_id; | 382 | thread->processor_id = processor_id; |
| 337 | thread->wait_type = WAITTYPE_NONE; | 383 | thread->wait_set_output = false; |
| 338 | thread->wait_object = nullptr; | 384 | thread->wait_all = false; |
| 385 | thread->wait_objects.clear(); | ||
| 339 | thread->wait_address = 0; | 386 | thread->wait_address = 0; |
| 340 | thread->name = std::move(name); | 387 | thread->name = std::move(name); |
| 341 | 388 | ||
| @@ -419,13 +466,20 @@ void Reschedule() { | |||
| 419 | LOG_TRACE(Kernel, "cannot context switch from 0x%08X, no higher priority thread!", prev->GetHandle()); | 466 | LOG_TRACE(Kernel, "cannot context switch from 0x%08X, no higher priority thread!", prev->GetHandle()); |
| 420 | 467 | ||
| 421 | for (auto& thread : thread_list) { | 468 | for (auto& thread : thread_list) { |
| 422 | LOG_TRACE(Kernel, "\thandle=0x%08X prio=0x%02X, status=0x%08X wait_type=0x%08X wait_handle=0x%08X", | 469 | LOG_TRACE(Kernel, "\thandle=0x%08X prio=0x%02X, status=0x%08X", thread->GetHandle(), |
| 423 | thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type, | 470 | thread->current_priority, thread->status); |
| 424 | (thread->wait_object ? thread->wait_object->GetHandle() : INVALID_HANDLE)); | ||
| 425 | } | 471 | } |
| 426 | } | 472 | } |
| 427 | } | 473 | } |
| 428 | 474 | ||
| 475 | void Thread::SetWaitSynchronizationResult(ResultCode result) { | ||
| 476 | context.cpu_registers[0] = result.raw; | ||
| 477 | } | ||
| 478 | |||
| 479 | void Thread::SetWaitSynchronizationOutput(s32 output) { | ||
| 480 | context.cpu_registers[1] = output; | ||
| 481 | } | ||
| 482 | |||
| 429 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 483 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 430 | 484 | ||
| 431 | void ThreadingInit() { | 485 | void ThreadingInit() { |