diff options
| author | 2015-06-17 18:07:15 -0400 | |
|---|---|---|
| committer | 2015-06-17 18:07:15 -0400 | |
| commit | 2e16edbc56c51265250269dd2d5e8b7b71335656 (patch) | |
| tree | 7634b833a4c5a647a172f29cff281920ce0f07ed /src/core/hle/kernel/thread.cpp | |
| parent | Merge pull request #864 from linkmauve/gl-info (diff) | |
| parent | kernel: Fix svcWaitSynch to always acquire requested wait objects. (diff) | |
| download | yuzu-2e16edbc56c51265250269dd2d5e8b7b71335656.tar.gz yuzu-2e16edbc56c51265250269dd2d5e8b7b71335656.tar.xz yuzu-2e16edbc56c51265250269dd2d5e8b7b71335656.zip | |
Merge pull request #849 from bunnei/fix-waitsynch-2
Fix svcWaitSynch to correctly acquire wait objects
Diffstat (limited to 'src/core/hle/kernel/thread.cpp')
| -rw-r--r-- | src/core/hle/kernel/thread.cpp | 76 |
1 files changed, 26 insertions, 50 deletions
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 22c795ad4..4729a7fe0 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #include "common/thread_queue_list.h" | 13 | #include "common/thread_queue_list.h" |
| 14 | 14 | ||
| 15 | #include "core/arm/arm_interface.h" | 15 | #include "core/arm/arm_interface.h" |
| 16 | #include "core/arm/skyeye_common/armdefs.h" | ||
| 16 | #include "core/core.h" | 17 | #include "core/core.h" |
| 17 | #include "core/core_timing.h" | 18 | #include "core/core_timing.h" |
| 18 | #include "core/hle/hle.h" | 19 | #include "core/hle/hle.h" |
| @@ -193,8 +194,22 @@ static void SwitchContext(Thread* new_thread) { | |||
| 193 | if (new_thread) { | 194 | if (new_thread) { |
| 194 | DEBUG_ASSERT_MSG(new_thread->status == THREADSTATUS_READY, "Thread must be ready to become running."); | 195 | DEBUG_ASSERT_MSG(new_thread->status == THREADSTATUS_READY, "Thread must be ready to become running."); |
| 195 | 196 | ||
| 197 | // Cancel any outstanding wakeup events for this thread | ||
| 198 | CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle); | ||
| 199 | |||
| 196 | current_thread = new_thread; | 200 | current_thread = new_thread; |
| 197 | 201 | ||
| 202 | // If the thread was waited by a svcWaitSynch call, step back PC by one instruction to rerun | ||
| 203 | // the SVC when the thread wakes up. This is necessary to ensure that the thread can acquire | ||
| 204 | // the requested wait object(s) before continuing. | ||
| 205 | if (new_thread->waitsynch_waited) { | ||
| 206 | // CPSR flag indicates CPU mode | ||
| 207 | bool thumb_mode = (new_thread->context.cpsr & TBIT) != 0; | ||
| 208 | |||
| 209 | // SVC instruction is 2 bytes for THUMB, 4 bytes for ARM | ||
| 210 | new_thread->context.pc -= thumb_mode ? 2 : 4; | ||
| 211 | } | ||
| 212 | |||
| 198 | ready_queue.remove(new_thread->current_priority, new_thread); | 213 | ready_queue.remove(new_thread->current_priority, new_thread); |
| 199 | new_thread->status = THREADSTATUS_RUNNING; | 214 | new_thread->status = THREADSTATUS_RUNNING; |
| 200 | 215 | ||
| @@ -243,6 +258,7 @@ void WaitCurrentThread_WaitSynchronization(std::vector<SharedPtr<WaitObject>> wa | |||
| 243 | thread->wait_set_output = wait_set_output; | 258 | thread->wait_set_output = wait_set_output; |
| 244 | thread->wait_all = wait_all; | 259 | thread->wait_all = wait_all; |
| 245 | thread->wait_objects = std::move(wait_objects); | 260 | thread->wait_objects = std::move(wait_objects); |
| 261 | thread->waitsynch_waited = true; | ||
| 246 | thread->status = THREADSTATUS_WAIT_SYNCH; | 262 | thread->status = THREADSTATUS_WAIT_SYNCH; |
| 247 | } | 263 | } |
| 248 | 264 | ||
| @@ -268,6 +284,8 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { | |||
| 268 | return; | 284 | return; |
| 269 | } | 285 | } |
| 270 | 286 | ||
| 287 | thread->waitsynch_waited = false; | ||
| 288 | |||
| 271 | if (thread->status == THREADSTATUS_WAIT_SYNCH) { | 289 | if (thread->status == THREADSTATUS_WAIT_SYNCH) { |
| 272 | thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS, | 290 | thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS, |
| 273 | ErrorSummary::StatusChanged, ErrorLevel::Info)); | 291 | ErrorSummary::StatusChanged, ErrorLevel::Info)); |
| @@ -288,63 +306,20 @@ void Thread::WakeAfterDelay(s64 nanoseconds) { | |||
| 288 | CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, callback_handle); | 306 | CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, callback_handle); |
| 289 | } | 307 | } |
| 290 | 308 | ||
| 291 | void Thread::ReleaseWaitObject(WaitObject* wait_object) { | ||
| 292 | if (status != THREADSTATUS_WAIT_SYNCH || wait_objects.empty()) { | ||
| 293 | LOG_CRITICAL(Kernel, "thread is not waiting on any objects!"); | ||
| 294 | return; | ||
| 295 | } | ||
| 296 | |||
| 297 | // Remove this thread from the waiting object's thread list | ||
| 298 | wait_object->RemoveWaitingThread(this); | ||
| 299 | |||
| 300 | unsigned index = 0; | ||
| 301 | bool wait_all_failed = false; // Will be set to true if any object is unavailable | ||
| 302 | |||
| 303 | // Iterate through all waiting objects to check availability... | ||
| 304 | for (auto itr = wait_objects.begin(); itr != wait_objects.end(); ++itr) { | ||
| 305 | if ((*itr)->ShouldWait()) | ||
| 306 | wait_all_failed = true; | ||
| 307 | |||
| 308 | // The output should be the last index of wait_object | ||
| 309 | if (*itr == wait_object) | ||
| 310 | index = itr - wait_objects.begin(); | ||
| 311 | } | ||
| 312 | |||
| 313 | // If we are waiting on all objects... | ||
| 314 | if (wait_all) { | ||
| 315 | // Resume the thread only if all are available... | ||
| 316 | if (!wait_all_failed) { | ||
| 317 | SetWaitSynchronizationResult(RESULT_SUCCESS); | ||
| 318 | SetWaitSynchronizationOutput(-1); | ||
| 319 | |||
| 320 | ResumeFromWait(); | ||
| 321 | } | ||
| 322 | } else { | ||
| 323 | // Otherwise, resume | ||
| 324 | SetWaitSynchronizationResult(RESULT_SUCCESS); | ||
| 325 | |||
| 326 | if (wait_set_output) | ||
| 327 | SetWaitSynchronizationOutput(index); | ||
| 328 | |||
| 329 | ResumeFromWait(); | ||
| 330 | } | ||
| 331 | } | ||
| 332 | |||
| 333 | void Thread::ResumeFromWait() { | 309 | void Thread::ResumeFromWait() { |
| 334 | // Cancel any outstanding wakeup events for this thread | ||
| 335 | CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle); | ||
| 336 | |||
| 337 | switch (status) { | 310 | switch (status) { |
| 338 | case THREADSTATUS_WAIT_SYNCH: | 311 | case THREADSTATUS_WAIT_SYNCH: |
| 339 | // Remove this thread from all other WaitObjects | ||
| 340 | for (auto wait_object : wait_objects) | ||
| 341 | wait_object->RemoveWaitingThread(this); | ||
| 342 | break; | ||
| 343 | case THREADSTATUS_WAIT_ARB: | 312 | case THREADSTATUS_WAIT_ARB: |
| 344 | case THREADSTATUS_WAIT_SLEEP: | 313 | case THREADSTATUS_WAIT_SLEEP: |
| 345 | break; | 314 | break; |
| 346 | case THREADSTATUS_RUNNING: | 315 | |
| 347 | case THREADSTATUS_READY: | 316 | case THREADSTATUS_READY: |
| 317 | // If the thread is waiting on multiple wait objects, it might be awoken more than once | ||
| 318 | // before actually resuming. We can ignore subsequent wakeups if the thread status has | ||
| 319 | // already been set to THREADSTATUS_READY. | ||
| 320 | return; | ||
| 321 | |||
| 322 | case THREADSTATUS_RUNNING: | ||
| 348 | DEBUG_ASSERT_MSG(false, "Thread with object id %u has already resumed.", GetObjectId()); | 323 | DEBUG_ASSERT_MSG(false, "Thread with object id %u has already resumed.", GetObjectId()); |
| 349 | return; | 324 | return; |
| 350 | case THREADSTATUS_DEAD: | 325 | case THREADSTATUS_DEAD: |
| @@ -415,6 +390,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, | |||
| 415 | thread->callback_handle = wakeup_callback_handle_table.Create(thread).MoveFrom(); | 390 | thread->callback_handle = wakeup_callback_handle_table.Create(thread).MoveFrom(); |
| 416 | thread->owner_process = g_current_process; | 391 | thread->owner_process = g_current_process; |
| 417 | thread->tls_index = -1; | 392 | thread->tls_index = -1; |
| 393 | thread->waitsynch_waited = false; | ||
| 418 | 394 | ||
| 419 | // Find the next available TLS index, and mark it as used | 395 | // Find the next available TLS index, and mark it as used |
| 420 | auto& used_tls_slots = Kernel::g_current_process->used_tls_slots; | 396 | auto& used_tls_slots = Kernel::g_current_process->used_tls_slots; |