summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Subv2018-01-08 11:35:03 -0500
committerGravatar bunnei2018-01-08 21:12:49 -0500
commit2a3f8e8484fca54767c9874cc21f5985d2be1463 (patch)
tree0976e02e0b495f07b11a51811618199d791d4c4e /src
parentcmake: Use LIBUNICORN_* on Windows. (diff)
downloadyuzu-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.cpp56
-rw-r--r--src/core/hle/kernel/thread.cpp25
-rw-r--r--src/core/hle/kernel/thread.h10
-rw-r--r--src/core/hle/kernel/wait_object.cpp17
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
123static void DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread, 123static 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
263void Thread::WakeAfterDelay(s64 nanoseconds) { 266void 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
274void Thread::CancelWakeupTimer() {
275 CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle);
276}
277
271void Thread::ResumeFromWait() { 278void 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 */
240SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority, SharedPtr<Process> owner_process); 243SharedPtr<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
70void WaitObject::WakeupWaitingThread(SharedPtr<Thread> thread) { 70void 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
93void WaitObject::WakeupAllWaitingThreads() { 102void WaitObject::WakeupAllWaitingThreads() {