diff options
| author | 2018-01-08 11:35:03 -0500 | |
|---|---|---|
| committer | 2018-01-08 21:12:49 -0500 | |
| commit | 2a3f8e8484fca54767c9874cc21f5985d2be1463 (patch) | |
| tree | 0976e02e0b495f07b11a51811618199d791d4c4e /src | |
| parent | cmake: Use LIBUNICORN_* on Windows. (diff) | |
| download | yuzu-2a3f8e8484fca54767c9874cc21f5985d2be1463.tar.gz yuzu-2a3f8e8484fca54767c9874cc21f5985d2be1463.tar.xz yuzu-2a3f8e8484fca54767c9874cc21f5985d2be1463.zip | |
Kernel: Allow chaining WaitSynchronization calls inside a wakeup callback.
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/hle/kernel/svc.cpp | 56 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.cpp | 25 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.h | 10 | ||||
| -rw-r--r-- | src/core/hle/kernel/wait_object.cpp | 17 |
4 files changed, 78 insertions, 30 deletions
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 74643f598..73793955a 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -120,17 +120,19 @@ static ResultCode GetProcessId(u32* process_id, Handle process_handle) { | |||
| 120 | } | 120 | } |
| 121 | 121 | ||
| 122 | /// Default thread wakeup callback for WaitSynchronization | 122 | /// Default thread wakeup callback for WaitSynchronization |
| 123 | static void DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread, | 123 | static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread, |
| 124 | SharedPtr<WaitObject> object) { | 124 | SharedPtr<WaitObject> object, size_t index) { |
| 125 | ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY); | 125 | ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY); |
| 126 | 126 | ||
| 127 | if (reason == ThreadWakeupReason::Timeout) { | 127 | if (reason == ThreadWakeupReason::Timeout) { |
| 128 | thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); | 128 | thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); |
| 129 | return; | 129 | return true; |
| 130 | } | 130 | } |
| 131 | 131 | ||
| 132 | ASSERT(reason == ThreadWakeupReason::Signal); | 132 | ASSERT(reason == ThreadWakeupReason::Signal); |
| 133 | thread->SetWaitSynchronizationResult(RESULT_SUCCESS); | 133 | thread->SetWaitSynchronizationResult(RESULT_SUCCESS); |
| 134 | |||
| 135 | return true; | ||
| 134 | }; | 136 | }; |
| 135 | 137 | ||
| 136 | /// Wait for a kernel object to synchronize, timeout after the specified nanoseconds | 138 | /// Wait for a kernel object to synchronize, timeout after the specified nanoseconds |
| @@ -499,20 +501,44 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr semaphore_add | |||
| 499 | ASSERT(semaphore->available_count == 0); | 501 | ASSERT(semaphore->available_count == 0); |
| 500 | ASSERT(semaphore->mutex_addr == mutex_addr); | 502 | ASSERT(semaphore->mutex_addr == mutex_addr); |
| 501 | 503 | ||
| 502 | CASCADE_CODE(WaitSynchronization1( | 504 | auto wakeup_callback = [mutex, nano_seconds](ThreadWakeupReason reason, |
| 503 | semaphore, thread.get(), nano_seconds, | 505 | SharedPtr<Thread> thread, |
| 504 | [mutex](ThreadWakeupReason reason, SharedPtr<Thread> thread, SharedPtr<WaitObject> object) { | 506 | SharedPtr<WaitObject> object, size_t index) { |
| 505 | ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY); | 507 | ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY); |
| 508 | |||
| 509 | if (reason == ThreadWakeupReason::Timeout) { | ||
| 510 | thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); | ||
| 511 | return true; | ||
| 512 | } | ||
| 513 | |||
| 514 | ASSERT(reason == ThreadWakeupReason::Signal); | ||
| 515 | |||
| 516 | // Now try to acquire the mutex and don't resume if it's not available. | ||
| 517 | if (!mutex->ShouldWait(thread.get())) { | ||
| 518 | mutex->Acquire(thread.get()); | ||
| 519 | thread->SetWaitSynchronizationResult(RESULT_SUCCESS); | ||
| 520 | return true; | ||
| 521 | } | ||
| 506 | 522 | ||
| 507 | if (reason == ThreadWakeupReason::Timeout) { | 523 | if (nano_seconds == 0) { |
| 508 | thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); | 524 | thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); |
| 509 | return; | 525 | return true; |
| 510 | } | 526 | } |
| 527 | |||
| 528 | thread->wait_objects = {mutex}; | ||
| 529 | mutex->AddWaitingThread(thread); | ||
| 530 | thread->status = THREADSTATUS_WAIT_SYNCH_ANY; | ||
| 531 | |||
| 532 | // Create an event to wake the thread up after the | ||
| 533 | // specified nanosecond delay has passed | ||
| 534 | thread->WakeAfterDelay(nano_seconds); | ||
| 535 | thread->wakeup_callback = DefaultThreadWakeupCallback; | ||
| 536 | |||
| 537 | Core::System::GetInstance().PrepareReschedule(); | ||
| 511 | 538 | ||
| 512 | ASSERT(reason == ThreadWakeupReason::Signal); | 539 | return false; |
| 513 | thread->SetWaitSynchronizationResult(WaitSynchronization1(mutex, thread.get())); | 540 | }; |
| 514 | thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(object.get())); | 541 | CASCADE_CODE(WaitSynchronization1(semaphore, thread.get(), nano_seconds, wakeup_callback)); |
| 515 | })); | ||
| 516 | 542 | ||
| 517 | mutex->Release(thread.get()); | 543 | mutex->Release(thread.get()); |
| 518 | 544 | ||
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index f9d821a80..111c496b9 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -244,20 +244,23 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { | |||
| 244 | return; | 244 | return; |
| 245 | } | 245 | } |
| 246 | 246 | ||
| 247 | bool resume = true; | ||
| 248 | |||
| 247 | if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY || | 249 | if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY || |
| 248 | thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB) { | 250 | thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB) { |
| 249 | 251 | ||
| 250 | // Invoke the wakeup callback before clearing the wait objects | ||
| 251 | if (thread->wakeup_callback) | ||
| 252 | thread->wakeup_callback(ThreadWakeupReason::Timeout, thread, nullptr); | ||
| 253 | |||
| 254 | // Remove the thread from each of its waiting objects' waitlists | 252 | // Remove the thread from each of its waiting objects' waitlists |
| 255 | for (auto& object : thread->wait_objects) | 253 | for (auto& object : thread->wait_objects) |
| 256 | object->RemoveWaitingThread(thread.get()); | 254 | object->RemoveWaitingThread(thread.get()); |
| 257 | thread->wait_objects.clear(); | 255 | thread->wait_objects.clear(); |
| 256 | |||
| 257 | // Invoke the wakeup callback before clearing the wait objects | ||
| 258 | if (thread->wakeup_callback) | ||
| 259 | resume = thread->wakeup_callback(ThreadWakeupReason::Timeout, thread, nullptr, 0); | ||
| 258 | } | 260 | } |
| 259 | 261 | ||
| 260 | thread->ResumeFromWait(); | 262 | if (resume) |
| 263 | thread->ResumeFromWait(); | ||
| 261 | } | 264 | } |
| 262 | 265 | ||
| 263 | void Thread::WakeAfterDelay(s64 nanoseconds) { | 266 | void Thread::WakeAfterDelay(s64 nanoseconds) { |
| @@ -268,6 +271,10 @@ void Thread::WakeAfterDelay(s64 nanoseconds) { | |||
| 268 | CoreTiming::ScheduleEvent(nsToCycles(nanoseconds), ThreadWakeupEventType, callback_handle); | 271 | CoreTiming::ScheduleEvent(nsToCycles(nanoseconds), ThreadWakeupEventType, callback_handle); |
| 269 | } | 272 | } |
| 270 | 273 | ||
| 274 | void Thread::CancelWakeupTimer() { | ||
| 275 | CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle); | ||
| 276 | } | ||
| 277 | |||
| 271 | void Thread::ResumeFromWait() { | 278 | void Thread::ResumeFromWait() { |
| 272 | ASSERT_MSG(wait_objects.empty(), "Thread is waking up while waiting for objects"); | 279 | ASSERT_MSG(wait_objects.empty(), "Thread is waking up while waiting for objects"); |
| 273 | 280 | ||
| @@ -444,7 +451,8 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, | |||
| 444 | // Map the page to the current process' address space. | 451 | // Map the page to the current process' address space. |
| 445 | // TODO(Subv): Find the correct MemoryState for this region. | 452 | // TODO(Subv): Find the correct MemoryState for this region. |
| 446 | vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE, | 453 | vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE, |
| 447 | linheap_memory, offset, Memory::PAGE_SIZE, MemoryState::ThreadLocalStorage); | 454 | linheap_memory, offset, Memory::PAGE_SIZE, |
| 455 | MemoryState::ThreadLocalStorage); | ||
| 448 | } | 456 | } |
| 449 | 457 | ||
| 450 | // Mark the slot as used | 458 | // Mark the slot as used |
| @@ -501,7 +509,8 @@ SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority, | |||
| 501 | SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); | 509 | SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); |
| 502 | 510 | ||
| 503 | // Register 1 must be a handle to the main thread | 511 | // Register 1 must be a handle to the main thread |
| 504 | thread->guest_handle = Kernel::g_handle_table.Create(thread).Unwrap();; | 512 | thread->guest_handle = Kernel::g_handle_table.Create(thread).Unwrap(); |
| 513 | |||
| 505 | thread->context.cpu_registers[1] = thread->guest_handle; | 514 | thread->context.cpu_registers[1] = thread->guest_handle; |
| 506 | 515 | ||
| 507 | // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires | 516 | // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires |
| @@ -572,4 +581,4 @@ const std::vector<SharedPtr<Thread>>& GetThreadList() { | |||
| 572 | return thread_list; | 581 | return thread_list; |
| 573 | } | 582 | } |
| 574 | 583 | ||
| 575 | } // namespace | 584 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index ed44ee933..19ba6e0af 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -128,6 +128,9 @@ public: | |||
| 128 | */ | 128 | */ |
| 129 | void WakeAfterDelay(s64 nanoseconds); | 129 | void WakeAfterDelay(s64 nanoseconds); |
| 130 | 130 | ||
| 131 | /// Cancel any outstanding wakeup events for this thread | ||
| 132 | void CancelWakeupTimer(); | ||
| 133 | |||
| 131 | /** | 134 | /** |
| 132 | * Sets the result after the thread awakens (from either WaitSynchronization SVC) | 135 | * Sets the result after the thread awakens (from either WaitSynchronization SVC) |
| 133 | * @param result Value to set to the returned result | 136 | * @param result Value to set to the returned result |
| @@ -218,8 +221,8 @@ public: | |||
| 218 | /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. | 221 | /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. |
| 219 | Handle callback_handle; | 222 | Handle callback_handle; |
| 220 | 223 | ||
| 221 | using WakeupCallback = void(ThreadWakeupReason reason, SharedPtr<Thread> thread, | 224 | using WakeupCallback = bool(ThreadWakeupReason reason, SharedPtr<Thread> thread, |
| 222 | SharedPtr<WaitObject> object); | 225 | SharedPtr<WaitObject> object, size_t index); |
| 223 | // Callback that will be invoked when the thread is resumed from a waiting state. If the thread | 226 | // Callback that will be invoked when the thread is resumed from a waiting state. If the thread |
| 224 | // was waiting via WaitSynchronizationN then the object will be the last object that became | 227 | // was waiting via WaitSynchronizationN then the object will be the last object that became |
| 225 | // available. In case of a timeout, the object will be nullptr. | 228 | // available. In case of a timeout, the object will be nullptr. |
| @@ -237,7 +240,8 @@ private: | |||
| 237 | * @param owner_process The parent process for the main thread | 240 | * @param owner_process The parent process for the main thread |
| 238 | * @return A shared pointer to the main thread | 241 | * @return A shared pointer to the main thread |
| 239 | */ | 242 | */ |
| 240 | SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority, SharedPtr<Process> owner_process); | 243 | SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority, |
| 244 | SharedPtr<Process> owner_process); | ||
| 241 | 245 | ||
| 242 | /** | 246 | /** |
| 243 | * Returns whether there are any threads that are ready to run. | 247 | * Returns whether there are any threads that are ready to run. |
diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp index c942a40fa..ec147b84c 100644 --- a/src/core/hle/kernel/wait_object.cpp +++ b/src/core/hle/kernel/wait_object.cpp | |||
| @@ -68,6 +68,8 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { | |||
| 68 | } | 68 | } |
| 69 | 69 | ||
| 70 | void WaitObject::WakeupWaitingThread(SharedPtr<Thread> thread) { | 70 | void WaitObject::WakeupWaitingThread(SharedPtr<Thread> thread) { |
| 71 | ASSERT(!ShouldWait(thread.get())); | ||
| 72 | |||
| 71 | if (!thread) | 73 | if (!thread) |
| 72 | return; | 74 | return; |
| 73 | 75 | ||
| @@ -75,19 +77,26 @@ void WaitObject::WakeupWaitingThread(SharedPtr<Thread> thread) { | |||
| 75 | Acquire(thread.get()); | 77 | Acquire(thread.get()); |
| 76 | } else { | 78 | } else { |
| 77 | for (auto& object : thread->wait_objects) { | 79 | for (auto& object : thread->wait_objects) { |
| 80 | ASSERT(!object->ShouldWait(thread.get())); | ||
| 78 | object->Acquire(thread.get()); | 81 | object->Acquire(thread.get()); |
| 79 | } | 82 | } |
| 80 | } | 83 | } |
| 81 | 84 | ||
| 82 | // Invoke the wakeup callback before clearing the wait objects | 85 | size_t index = thread->GetWaitObjectIndex(this); |
| 83 | if (thread->wakeup_callback) | ||
| 84 | thread->wakeup_callback(ThreadWakeupReason::Signal, thread, this); | ||
| 85 | 86 | ||
| 86 | for (auto& object : thread->wait_objects) | 87 | for (auto& object : thread->wait_objects) |
| 87 | object->RemoveWaitingThread(thread.get()); | 88 | object->RemoveWaitingThread(thread.get()); |
| 88 | thread->wait_objects.clear(); | 89 | thread->wait_objects.clear(); |
| 89 | 90 | ||
| 90 | thread->ResumeFromWait(); | 91 | thread->CancelWakeupTimer(); |
| 92 | |||
| 93 | bool resume = true; | ||
| 94 | |||
| 95 | if (thread->wakeup_callback) | ||
| 96 | resume = thread->wakeup_callback(ThreadWakeupReason::Signal, thread, this, index); | ||
| 97 | |||
| 98 | if (resume) | ||
| 99 | thread->ResumeFromWait(); | ||
| 91 | } | 100 | } |
| 92 | 101 | ||
| 93 | void WaitObject::WakeupAllWaitingThreads() { | 102 | void WaitObject::WakeupAllWaitingThreads() { |