diff options
| author | 2018-04-20 14:39:28 -0500 | |
|---|---|---|
| committer | 2018-04-20 21:04:27 -0500 | |
| commit | b18ccf9399430a91790a93a122d5cd05266301ab (patch) | |
| tree | 4246d037b72e9b80ef963c8be48f5945e6b8f592 /src/core/hle/kernel/svc.cpp | |
| parent | Kernel: Corrected the implementation of svcArbitrateLock and svcArbitrateUnlock. (diff) | |
| download | yuzu-b18ccf9399430a91790a93a122d5cd05266301ab.tar.gz yuzu-b18ccf9399430a91790a93a122d5cd05266301ab.tar.xz yuzu-b18ccf9399430a91790a93a122d5cd05266301ab.zip | |
Kernel: Properly implemented svcWaitProcessWideKey and svcSignalProcessWideKey
They work in tandem with guest code to provide synchronization primitives along with svcArbitrateLock/Unlock
Diffstat (limited to 'src/core/hle/kernel/svc.cpp')
| -rw-r--r-- | src/core/hle/kernel/svc.cpp | 129 |
1 files changed, 46 insertions, 83 deletions
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 92273b488..99c1c2d2a 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -616,77 +616,18 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var | |||
| 616 | SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle); | 616 | SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle); |
| 617 | ASSERT(thread); | 617 | ASSERT(thread); |
| 618 | 618 | ||
| 619 | SharedPtr<Mutex> mutex = g_object_address_table.Get<Mutex>(mutex_addr); | 619 | CASCADE_CODE(Mutex::Release(mutex_addr)); |
| 620 | if (!mutex) { | ||
| 621 | // Create a new mutex for the specified address if one does not already exist | ||
| 622 | mutex = Mutex::Create(thread, mutex_addr); | ||
| 623 | mutex->name = Common::StringFromFormat("mutex-%llx", mutex_addr); | ||
| 624 | } | ||
| 625 | |||
| 626 | SharedPtr<ConditionVariable> condition_variable = | ||
| 627 | g_object_address_table.Get<ConditionVariable>(condition_variable_addr); | ||
| 628 | if (!condition_variable) { | ||
| 629 | // Create a new condition_variable for the specified address if one does not already exist | ||
| 630 | condition_variable = ConditionVariable::Create(condition_variable_addr).Unwrap(); | ||
| 631 | condition_variable->name = | ||
| 632 | Common::StringFromFormat("condition-variable-%llx", condition_variable_addr); | ||
| 633 | } | ||
| 634 | |||
| 635 | if (condition_variable->mutex_addr) { | ||
| 636 | // Previously created the ConditionVariable using WaitProcessWideKeyAtomic, verify | ||
| 637 | // everything is correct | ||
| 638 | ASSERT(condition_variable->mutex_addr == mutex_addr); | ||
| 639 | } else { | ||
| 640 | // Previously created the ConditionVariable using SignalProcessWideKey, set the mutex | ||
| 641 | // associated with it | ||
| 642 | condition_variable->mutex_addr = mutex_addr; | ||
| 643 | } | ||
| 644 | |||
| 645 | if (mutex->GetOwnerHandle()) { | ||
| 646 | // Release the mutex if the current thread is holding it | ||
| 647 | mutex->Release(thread.get()); | ||
| 648 | } | ||
| 649 | |||
| 650 | auto wakeup_callback = [mutex, nano_seconds](ThreadWakeupReason reason, | ||
| 651 | SharedPtr<Thread> thread, | ||
| 652 | SharedPtr<WaitObject> object, size_t index) { | ||
| 653 | ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY); | ||
| 654 | |||
| 655 | if (reason == ThreadWakeupReason::Timeout) { | ||
| 656 | thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); | ||
| 657 | return true; | ||
| 658 | } | ||
| 659 | |||
| 660 | ASSERT(reason == ThreadWakeupReason::Signal); | ||
| 661 | |||
| 662 | // Now try to acquire the mutex and don't resume if it's not available. | ||
| 663 | if (!mutex->ShouldWait(thread.get())) { | ||
| 664 | mutex->Acquire(thread.get()); | ||
| 665 | thread->SetWaitSynchronizationResult(RESULT_SUCCESS); | ||
| 666 | return true; | ||
| 667 | } | ||
| 668 | |||
| 669 | if (nano_seconds == 0) { | ||
| 670 | thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); | ||
| 671 | return true; | ||
| 672 | } | ||
| 673 | |||
| 674 | thread->wait_objects = {mutex}; | ||
| 675 | mutex->AddWaitingThread(thread); | ||
| 676 | thread->status = THREADSTATUS_WAIT_SYNCH_ANY; | ||
| 677 | |||
| 678 | // Create an event to wake the thread up after the | ||
| 679 | // specified nanosecond delay has passed | ||
| 680 | thread->WakeAfterDelay(nano_seconds); | ||
| 681 | thread->wakeup_callback = DefaultThreadWakeupCallback; | ||
| 682 | 620 | ||
| 683 | Core::System::GetInstance().PrepareReschedule(); | 621 | SharedPtr<Thread> current_thread = GetCurrentThread(); |
| 622 | current_thread->condvar_wait_address = condition_variable_addr; | ||
| 623 | current_thread->mutex_wait_address = mutex_addr; | ||
| 624 | current_thread->wait_handle = thread_handle; | ||
| 625 | current_thread->status = THREADSTATUS_WAIT_MUTEX; | ||
| 626 | current_thread->wakeup_callback = nullptr; | ||
| 684 | 627 | ||
| 685 | return false; | 628 | current_thread->WakeAfterDelay(nano_seconds); |
| 686 | }; | ||
| 687 | CASCADE_CODE( | ||
| 688 | WaitSynchronization1(condition_variable, thread.get(), nano_seconds, wakeup_callback)); | ||
| 689 | 629 | ||
| 630 | Core::System::GetInstance().PrepareReschedule(); | ||
| 690 | return RESULT_SUCCESS; | 631 | return RESULT_SUCCESS; |
| 691 | } | 632 | } |
| 692 | 633 | ||
| @@ -695,24 +636,46 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target | |||
| 695 | LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x%llx, target=0x%08x", | 636 | LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x%llx, target=0x%08x", |
| 696 | condition_variable_addr, target); | 637 | condition_variable_addr, target); |
| 697 | 638 | ||
| 698 | // Wakeup all or one thread - Any other value is unimplemented | 639 | u32 processed = 0; |
| 699 | ASSERT(target == -1 || target == 1); | 640 | auto& thread_list = Core::System::GetInstance().Scheduler().GetThreadList(); |
| 641 | |||
| 642 | for (auto& thread : thread_list) { | ||
| 643 | if (thread->condvar_wait_address != condition_variable_addr) | ||
| 644 | continue; | ||
| 700 | 645 | ||
| 701 | SharedPtr<ConditionVariable> condition_variable = | 646 | // Only process up to 'target' threads, unless 'target' is -1, in which case process |
| 702 | g_object_address_table.Get<ConditionVariable>(condition_variable_addr); | 647 | // them all. |
| 703 | if (!condition_variable) { | 648 | if (target != -1 && processed >= target) |
| 704 | // Create a new condition_variable for the specified address if one does not already exist | 649 | break; |
| 705 | condition_variable = ConditionVariable::Create(condition_variable_addr).Unwrap(); | ||
| 706 | condition_variable->name = | ||
| 707 | Common::StringFromFormat("condition-variable-%llx", condition_variable_addr); | ||
| 708 | } | ||
| 709 | 650 | ||
| 710 | CASCADE_CODE(condition_variable->Release(target)); | 651 | // If the mutex is not yet acquired, acquire it. |
| 652 | u32 mutex_val = Memory::Read32(thread->mutex_wait_address); | ||
| 653 | |||
| 654 | if (mutex_val == 0) { | ||
| 655 | // We were able to acquire the mutex, resume this thread. | ||
| 656 | Memory::Write32(thread->mutex_wait_address, thread->wait_handle); | ||
| 657 | ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX); | ||
| 658 | thread->ResumeFromWait(); | ||
| 659 | |||
| 660 | thread->mutex_wait_address = 0; | ||
| 661 | thread->condvar_wait_address = 0; | ||
| 662 | thread->wait_handle = 0; | ||
| 663 | } else { | ||
| 664 | // Couldn't acquire the mutex, block the thread. | ||
| 665 | Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); | ||
| 666 | auto owner = g_handle_table.Get<Thread>(owner_handle); | ||
| 667 | ASSERT(owner); | ||
| 668 | ASSERT(thread->status != THREADSTATUS_RUNNING); | ||
| 669 | thread->status = THREADSTATUS_WAIT_MUTEX; | ||
| 670 | thread->wakeup_callback = nullptr; | ||
| 671 | |||
| 672 | // Signal that the mutex now has a waiting thread. | ||
| 673 | Memory::Write32(thread->mutex_wait_address, mutex_val | Mutex::MutexHasWaitersFlag); | ||
| 674 | |||
| 675 | Core::System::GetInstance().PrepareReschedule(); | ||
| 676 | } | ||
| 711 | 677 | ||
| 712 | if (condition_variable->mutex_addr) { | 678 | ++processed; |
| 713 | // If a mutex was created for this condition_variable, wait the current thread on it | ||
| 714 | SharedPtr<Mutex> mutex = g_object_address_table.Get<Mutex>(condition_variable->mutex_addr); | ||
| 715 | return WaitSynchronization1(mutex, GetCurrentThread()); | ||
| 716 | } | 679 | } |
| 717 | 680 | ||
| 718 | return RESULT_SUCCESS; | 681 | return RESULT_SUCCESS; |