summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
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 11f7d2127..2614a260c 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();
@@ -395,7 +403,6 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
395 thread->nominal_priority = thread->current_priority = priority; 403 thread->nominal_priority = thread->current_priority = priority;
396 thread->last_running_ticks = CoreTiming::GetTicks(); 404 thread->last_running_ticks = CoreTiming::GetTicks();
397 thread->processor_id = processor_id; 405 thread->processor_id = processor_id;
398 thread->wait_set_output = false;
399 thread->wait_objects.clear(); 406 thread->wait_objects.clear();
400 thread->wait_address = 0; 407 thread->wait_address = 0;
401 thread->name = std::move(name); 408 thread->name = std::move(name);
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index f02e1d43a..4679c2022 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;
@@ -199,14 +204,18 @@ public:
199 204
200 VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address 205 VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address
201 206
202 /// True if the WaitSynchronizationN output parameter should be set on thread wakeup.
203 bool wait_set_output;
204
205 std::string name; 207 std::string name;
206 208
207 /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. 209 /// Handle used as userdata to reference this object when inserting into the CoreTiming queue.
208 Handle callback_handle; 210 Handle callback_handle;
209 211
212 using WakeupCallback = void(ThreadWakeupReason reason, SharedPtr<Thread> thread,
213 SharedPtr<WaitObject> object);
214 // Callback that will be invoked when the thread is resumed from a waiting state. If the thread
215 // was waiting via WaitSynchronizationN then the object will be the last object that became
216 // available. In case of a timeout, the object will be nullptr.
217 std::function<WakeupCallback> wakeup_callback;
218
210private: 219private:
211 Thread(); 220 Thread();
212 ~Thread() override; 221 ~Thread() override;
diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp
index 56fdd977f..469554908 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 41c82c922..fefd50805 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}