summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Subv2017-09-28 11:53:32 -0500
committerGravatar Subv2017-09-28 11:53:32 -0500
commit8432749db7afecc9beea20f993cc036418caaa15 (patch)
tree10dd3fcd755bd7bdc27eef120885c7e700ab4288 /src
parentMerge pull request #2907 from Subv/warnings3 (diff)
downloadyuzu-8432749db7afecc9beea20f993cc036418caaa15.tar.gz
yuzu-8432749db7afecc9beea20f993cc036418caaa15.tar.xz
yuzu-8432749db7afecc9beea20f993cc036418caaa15.zip
Kernel/Threads: When putting a thread to wait, specify a function to execute when it is awoken.
This change makes for a clearer (less confusing) path of execution in the scheduler, now the code to execute when a thread awakes is closer to the code that puts the thread to sleep (WaitSynch1, WaitSynchN). It also allows us to implement the special wake up behavior of ReplyAndReceive without hacking up WaitObject::WakeupAllWaitingThreads. If savestates are desired in the future, we can change this implementation to one similar to the CoreTiming event system, where we first register the callback functions at startup and assign their identifiers to the Thread callback variable instead of directly assigning a lambda to the wake up callback variable.
Diffstat (limited to 'src')
-rw-r--r--src/core/hle/kernel/thread.cpp13
-rw-r--r--src/core/hle/kernel/thread.h15
-rw-r--r--src/core/hle/kernel/wait_object.cpp11
-rw-r--r--src/core/hle/svc.cpp69
4 files changed, 91 insertions, 17 deletions
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 61378211f..690cb20b3 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -247,12 +247,15 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
247 247
248 if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY || 248 if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY ||
249 thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB) { 249 thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB) {
250 thread->wait_set_output = false; 250
251 // Invoke the wakeup callback before clearing the wait objects
252 if (thread->wakeup_callback)
253 thread->wakeup_callback(ThreadWakeupReason::Timeout, thread, nullptr);
254
251 // Remove the thread from each of its waiting objects' waitlists 255 // Remove the thread from each of its waiting objects' waitlists
252 for (auto& object : thread->wait_objects) 256 for (auto& object : thread->wait_objects)
253 object->RemoveWaitingThread(thread.get()); 257 object->RemoveWaitingThread(thread.get());
254 thread->wait_objects.clear(); 258 thread->wait_objects.clear();
255 thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
256 } 259 }
257 260
258 thread->ResumeFromWait(); 261 thread->ResumeFromWait();
@@ -278,6 +281,9 @@ void Thread::ResumeFromWait() {
278 break; 281 break;
279 282
280 case THREADSTATUS_READY: 283 case THREADSTATUS_READY:
284 // The thread's wakeup callback must have already been cleared when the thread was first
285 // awoken.
286 ASSERT(wakeup_callback == nullptr);
281 // If the thread is waiting on multiple wait objects, it might be awoken more than once 287 // If the thread is waiting on multiple wait objects, it might be awoken more than once
282 // before actually resuming. We can ignore subsequent wakeups if the thread status has 288 // before actually resuming. We can ignore subsequent wakeups if the thread status has
283 // already been set to THREADSTATUS_READY. 289 // already been set to THREADSTATUS_READY.
@@ -293,6 +299,8 @@ void Thread::ResumeFromWait() {
293 return; 299 return;
294 } 300 }
295 301
302 wakeup_callback = nullptr;
303
296 ready_queue.push_back(current_priority, this); 304 ready_queue.push_back(current_priority, this);
297 status = THREADSTATUS_READY; 305 status = THREADSTATUS_READY;
298 Core::System::GetInstance().PrepareReschedule(); 306 Core::System::GetInstance().PrepareReschedule();
@@ -394,7 +402,6 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
394 thread->nominal_priority = thread->current_priority = priority; 402 thread->nominal_priority = thread->current_priority = priority;
395 thread->last_running_ticks = CoreTiming::GetTicks(); 403 thread->last_running_ticks = CoreTiming::GetTicks();
396 thread->processor_id = processor_id; 404 thread->processor_id = processor_id;
397 thread->wait_set_output = false;
398 thread->wait_objects.clear(); 405 thread->wait_objects.clear();
399 thread->wait_address = 0; 406 thread->wait_address = 0;
400 thread->name = std::move(name); 407 thread->name = std::move(name);
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 6a3566f15..328f1a86a 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -41,6 +41,11 @@ enum ThreadStatus {
41 THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated 41 THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated
42}; 42};
43 43
44enum class ThreadWakeupReason {
45 Signal, // The thread was woken up by WakeupAllWaitingThreads due to an object signal.
46 Timeout // The thread was woken up due to a wait timeout.
47};
48
44namespace Kernel { 49namespace Kernel {
45 50
46class Mutex; 51class Mutex;
@@ -197,14 +202,18 @@ public:
197 202
198 VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address 203 VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address
199 204
200 /// True if the WaitSynchronizationN output parameter should be set on thread wakeup.
201 bool wait_set_output;
202
203 std::string name; 205 std::string name;
204 206
205 /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. 207 /// Handle used as userdata to reference this object when inserting into the CoreTiming queue.
206 Handle callback_handle; 208 Handle callback_handle;
207 209
210 using WakeupCallback = void(ThreadWakeupReason reason, SharedPtr<Thread> thread,
211 SharedPtr<WaitObject> object);
212 // Callback that will be invoked when the thread is resumed from a waiting state. If the thread
213 // was waiting via WaitSynchronizationN then the object will be the last object that became
214 // available. In case of a timeout, the object will be nullptr.
215 std::function<WakeupCallback> wakeup_callback;
216
208private: 217private:
209 Thread(); 218 Thread();
210 ~Thread() override; 219 ~Thread() override;
diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp
index f245eda6c..1ced26905 100644
--- a/src/core/hle/kernel/wait_object.cpp
+++ b/src/core/hle/kernel/wait_object.cpp
@@ -71,23 +71,20 @@ void WaitObject::WakeupAllWaitingThreads() {
71 while (auto thread = GetHighestPriorityReadyThread()) { 71 while (auto thread = GetHighestPriorityReadyThread()) {
72 if (!thread->IsSleepingOnWaitAll()) { 72 if (!thread->IsSleepingOnWaitAll()) {
73 Acquire(thread.get()); 73 Acquire(thread.get());
74 // Set the output index of the WaitSynchronizationN call to the index of this object.
75 if (thread->wait_set_output) {
76 thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(this));
77 thread->wait_set_output = false;
78 }
79 } else { 74 } else {
80 for (auto& object : thread->wait_objects) { 75 for (auto& object : thread->wait_objects) {
81 object->Acquire(thread.get()); 76 object->Acquire(thread.get());
82 } 77 }
83 // Note: This case doesn't update the output index of WaitSynchronizationN.
84 } 78 }
85 79
80 // Invoke the wakeup callback before clearing the wait objects
81 if (thread->wakeup_callback)
82 thread->wakeup_callback(ThreadWakeupReason::Signal, thread, this);
83
86 for (auto& object : thread->wait_objects) 84 for (auto& object : thread->wait_objects)
87 object->RemoveWaitingThread(thread.get()); 85 object->RemoveWaitingThread(thread.get());
88 thread->wait_objects.clear(); 86 thread->wait_objects.clear();
89 87
90 thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
91 thread->ResumeFromWait(); 88 thread->ResumeFromWait();
92 } 89 }
93} 90}
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index dfc36748c..41e62cf62 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -271,6 +271,24 @@ static ResultCode WaitSynchronization1(Kernel::Handle handle, s64 nano_seconds)
271 // Create an event to wake the thread up after the specified nanosecond delay has passed 271 // Create an event to wake the thread up after the specified nanosecond delay has passed
272 thread->WakeAfterDelay(nano_seconds); 272 thread->WakeAfterDelay(nano_seconds);
273 273
274 thread->wakeup_callback = [](ThreadWakeupReason reason,
275 Kernel::SharedPtr<Kernel::Thread> thread,
276 Kernel::SharedPtr<Kernel::WaitObject> object) {
277
278 ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY);
279
280 if (reason == ThreadWakeupReason::Timeout) {
281 thread->SetWaitSynchronizationResult(Kernel::RESULT_TIMEOUT);
282 return;
283 }
284
285 ASSERT(reason == ThreadWakeupReason::Signal);
286 thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
287
288 // WaitSynchronization1 doesn't have an output index like WaitSynchronizationN, so we
289 // don't have to do anything else here.
290 };
291
274 Core::System::GetInstance().PrepareReschedule(); 292 Core::System::GetInstance().PrepareReschedule();
275 293
276 // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread 294 // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread
@@ -344,6 +362,23 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha
344 // Create an event to wake the thread up after the specified nanosecond delay has passed 362 // Create an event to wake the thread up after the specified nanosecond delay has passed
345 thread->WakeAfterDelay(nano_seconds); 363 thread->WakeAfterDelay(nano_seconds);
346 364
365 thread->wakeup_callback = [](ThreadWakeupReason reason,
366 Kernel::SharedPtr<Kernel::Thread> thread,
367 Kernel::SharedPtr<Kernel::WaitObject> object) {
368
369 ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ALL);
370
371 if (reason == ThreadWakeupReason::Timeout) {
372 thread->SetWaitSynchronizationResult(Kernel::RESULT_TIMEOUT);
373 return;
374 }
375
376 ASSERT(reason == ThreadWakeupReason::Signal);
377
378 thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
379 // The wait_all case does not update the output index.
380 };
381
347 Core::System::GetInstance().PrepareReschedule(); 382 Core::System::GetInstance().PrepareReschedule();
348 383
349 // This value gets set to -1 by default in this case, it is not modified after this. 384 // This value gets set to -1 by default in this case, it is not modified after this.
@@ -389,12 +424,28 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha
389 // Create an event to wake the thread up after the specified nanosecond delay has passed 424 // Create an event to wake the thread up after the specified nanosecond delay has passed
390 thread->WakeAfterDelay(nano_seconds); 425 thread->WakeAfterDelay(nano_seconds);
391 426
427 thread->wakeup_callback = [](ThreadWakeupReason reason,
428 Kernel::SharedPtr<Kernel::Thread> thread,
429 Kernel::SharedPtr<Kernel::WaitObject> object) {
430
431 ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY);
432
433 if (reason == ThreadWakeupReason::Timeout) {
434 thread->SetWaitSynchronizationResult(Kernel::RESULT_TIMEOUT);
435 return;
436 }
437
438 ASSERT(reason == ThreadWakeupReason::Signal);
439
440 thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
441 thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(object.get()));
442 };
443
392 Core::System::GetInstance().PrepareReschedule(); 444 Core::System::GetInstance().PrepareReschedule();
393 445
394 // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a 446 // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a
395 // signal in one of its wait objects. 447 // signal in one of its wait objects.
396 // Otherwise we retain the default value of timeout, and -1 in the out parameter 448 // Otherwise we retain the default value of timeout, and -1 in the out parameter
397 thread->wait_set_output = true;
398 *out = -1; 449 *out = -1;
399 return Kernel::RESULT_TIMEOUT; 450 return Kernel::RESULT_TIMEOUT;
400 } 451 }
@@ -483,8 +534,6 @@ static ResultCode ReplyAndReceive(s32* index, Kernel::Handle* handles, s32 handl
483 534
484 // No objects were ready to be acquired, prepare to suspend the thread. 535 // No objects were ready to be acquired, prepare to suspend the thread.
485 536
486 // TODO(Subv): Perform IPC translation upon wakeup.
487
488 // Put the thread to sleep 537 // Put the thread to sleep
489 thread->status = THREADSTATUS_WAIT_SYNCH_ANY; 538 thread->status = THREADSTATUS_WAIT_SYNCH_ANY;
490 539
@@ -496,12 +545,24 @@ static ResultCode ReplyAndReceive(s32* index, Kernel::Handle* handles, s32 handl
496 545
497 thread->wait_objects = std::move(objects); 546 thread->wait_objects = std::move(objects);
498 547
548 thread->wakeup_callback = [](ThreadWakeupReason reason,
549 Kernel::SharedPtr<Kernel::Thread> thread,
550 Kernel::SharedPtr<Kernel::WaitObject> object) {
551
552 ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY);
553 ASSERT(reason == ThreadWakeupReason::Signal);
554
555 thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
556 thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(object.get()));
557
558 // TODO(Subv): Perform IPC translation upon wakeup.
559 };
560
499 Core::System::GetInstance().PrepareReschedule(); 561 Core::System::GetInstance().PrepareReschedule();
500 562
501 // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a 563 // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a
502 // signal in one of its wait objects, or to 0xC8A01836 if there was a translation error. 564 // signal in one of its wait objects, or to 0xC8A01836 if there was a translation error.
503 // By default the index is set to -1. 565 // By default the index is set to -1.
504 thread->wait_set_output = true;
505 *index = -1; 566 *index = -1;
506 return RESULT_SUCCESS; 567 return RESULT_SUCCESS;
507} 568}