diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/hle/kernel/thread.cpp | 13 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.h | 15 | ||||
| -rw-r--r-- | src/core/hle/kernel/wait_object.cpp | 11 | ||||
| -rw-r--r-- | src/core/hle/svc.cpp | 69 |
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 | ||
| 44 | enum 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 | |||
| 44 | namespace Kernel { | 49 | namespace Kernel { |
| 45 | 50 | ||
| 46 | class Mutex; | 51 | class 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 | |||
| 210 | private: | 219 | private: |
| 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 | } |