diff options
| author | 2018-04-23 16:33:00 -0400 | |
|---|---|---|
| committer | 2018-04-23 16:33:00 -0400 | |
| commit | 0214351f4f0e9377792f8ceb657e3a47aba334d1 (patch) | |
| tree | f772d4dbaf3d804497740c6c67d1110f20fd010f /src/core/hle/kernel/svc.cpp | |
| parent | Merge pull request #384 from Subv/nvhost-remap (diff) | |
| parent | Kernel: Implemented mutex priority inheritance. (diff) | |
| download | yuzu-0214351f4f0e9377792f8ceb657e3a47aba334d1.tar.gz yuzu-0214351f4f0e9377792f8ceb657e3a47aba334d1.tar.xz yuzu-0214351f4f0e9377792f8ceb657e3a47aba334d1.zip | |
Merge pull request #370 from Subv/sync_primitives
Kernel: Reworked the new kernel synchronization primitives.
Diffstat (limited to 'src/core/hle/kernel/svc.cpp')
| -rw-r--r-- | src/core/hle/kernel/svc.cpp | 162 |
1 files changed, 55 insertions, 107 deletions
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 633740992..c22da6e47 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -13,7 +13,6 @@ | |||
| 13 | #include "core/core_timing.h" | 13 | #include "core/core_timing.h" |
| 14 | #include "core/hle/kernel/client_port.h" | 14 | #include "core/hle/kernel/client_port.h" |
| 15 | #include "core/hle/kernel/client_session.h" | 15 | #include "core/hle/kernel/client_session.h" |
| 16 | #include "core/hle/kernel/condition_variable.h" | ||
| 17 | #include "core/hle/kernel/event.h" | 16 | #include "core/hle/kernel/event.h" |
| 18 | #include "core/hle/kernel/handle_table.h" | 17 | #include "core/hle/kernel/handle_table.h" |
| 19 | #include "core/hle/kernel/mutex.h" | 18 | #include "core/hle/kernel/mutex.h" |
| @@ -262,32 +261,14 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr, | |||
| 262 | "requesting_current_thread_handle=0x%08X", | 261 | "requesting_current_thread_handle=0x%08X", |
| 263 | holding_thread_handle, mutex_addr, requesting_thread_handle); | 262 | holding_thread_handle, mutex_addr, requesting_thread_handle); |
| 264 | 263 | ||
| 265 | SharedPtr<Thread> holding_thread = g_handle_table.Get<Thread>(holding_thread_handle); | 264 | return Mutex::TryAcquire(mutex_addr, holding_thread_handle, requesting_thread_handle); |
| 266 | SharedPtr<Thread> requesting_thread = g_handle_table.Get<Thread>(requesting_thread_handle); | ||
| 267 | |||
| 268 | ASSERT(requesting_thread); | ||
| 269 | ASSERT(requesting_thread == GetCurrentThread()); | ||
| 270 | |||
| 271 | SharedPtr<Mutex> mutex = g_object_address_table.Get<Mutex>(mutex_addr); | ||
| 272 | if (!mutex) { | ||
| 273 | // Create a new mutex for the specified address if one does not already exist | ||
| 274 | mutex = Mutex::Create(holding_thread, mutex_addr); | ||
| 275 | mutex->name = Common::StringFromFormat("mutex-%llx", mutex_addr); | ||
| 276 | } | ||
| 277 | |||
| 278 | ASSERT(holding_thread == mutex->GetHoldingThread()); | ||
| 279 | |||
| 280 | return WaitSynchronization1(mutex, requesting_thread.get()); | ||
| 281 | } | 265 | } |
| 282 | 266 | ||
| 283 | /// Unlock a mutex | 267 | /// Unlock a mutex |
| 284 | static ResultCode ArbitrateUnlock(VAddr mutex_addr) { | 268 | static ResultCode ArbitrateUnlock(VAddr mutex_addr) { |
| 285 | LOG_TRACE(Kernel_SVC, "called mutex_addr=0x%llx", mutex_addr); | 269 | LOG_TRACE(Kernel_SVC, "called mutex_addr=0x%llx", mutex_addr); |
| 286 | 270 | ||
| 287 | SharedPtr<Mutex> mutex = g_object_address_table.Get<Mutex>(mutex_addr); | 271 | return Mutex::Release(mutex_addr); |
| 288 | ASSERT(mutex); | ||
| 289 | |||
| 290 | return mutex->Release(GetCurrentThread()); | ||
| 291 | } | 272 | } |
| 292 | 273 | ||
| 293 | /// Break program execution | 274 | /// Break program execution |
| @@ -412,11 +393,6 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) { | |||
| 412 | } | 393 | } |
| 413 | 394 | ||
| 414 | thread->SetPriority(priority); | 395 | thread->SetPriority(priority); |
| 415 | thread->UpdatePriority(); | ||
| 416 | |||
| 417 | // Update the mutexes that this thread is waiting for | ||
| 418 | for (auto& mutex : thread->pending_mutexes) | ||
| 419 | mutex->UpdatePriority(); | ||
| 420 | 396 | ||
| 421 | Core::System::GetInstance().PrepareReschedule(); | 397 | Core::System::GetInstance().PrepareReschedule(); |
| 422 | return RESULT_SUCCESS; | 398 | return RESULT_SUCCESS; |
| @@ -634,103 +610,75 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var | |||
| 634 | SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle); | 610 | SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle); |
| 635 | ASSERT(thread); | 611 | ASSERT(thread); |
| 636 | 612 | ||
| 637 | SharedPtr<Mutex> mutex = g_object_address_table.Get<Mutex>(mutex_addr); | 613 | CASCADE_CODE(Mutex::Release(mutex_addr)); |
| 638 | if (!mutex) { | ||
| 639 | // Create a new mutex for the specified address if one does not already exist | ||
| 640 | mutex = Mutex::Create(thread, mutex_addr); | ||
| 641 | mutex->name = Common::StringFromFormat("mutex-%llx", mutex_addr); | ||
| 642 | } | ||
| 643 | |||
| 644 | SharedPtr<ConditionVariable> condition_variable = | ||
| 645 | g_object_address_table.Get<ConditionVariable>(condition_variable_addr); | ||
| 646 | if (!condition_variable) { | ||
| 647 | // Create a new condition_variable for the specified address if one does not already exist | ||
| 648 | condition_variable = ConditionVariable::Create(condition_variable_addr).Unwrap(); | ||
| 649 | condition_variable->name = | ||
| 650 | Common::StringFromFormat("condition-variable-%llx", condition_variable_addr); | ||
| 651 | } | ||
| 652 | |||
| 653 | if (condition_variable->mutex_addr) { | ||
| 654 | // Previously created the ConditionVariable using WaitProcessWideKeyAtomic, verify | ||
| 655 | // everything is correct | ||
| 656 | ASSERT(condition_variable->mutex_addr == mutex_addr); | ||
| 657 | } else { | ||
| 658 | // Previously created the ConditionVariable using SignalProcessWideKey, set the mutex | ||
| 659 | // associated with it | ||
| 660 | condition_variable->mutex_addr = mutex_addr; | ||
| 661 | } | ||
| 662 | 614 | ||
| 663 | if (mutex->GetOwnerHandle()) { | 615 | SharedPtr<Thread> current_thread = GetCurrentThread(); |
| 664 | // Release the mutex if the current thread is holding it | 616 | current_thread->condvar_wait_address = condition_variable_addr; |
| 665 | mutex->Release(thread.get()); | 617 | current_thread->mutex_wait_address = mutex_addr; |
| 666 | } | 618 | current_thread->wait_handle = thread_handle; |
| 619 | current_thread->status = THREADSTATUS_WAIT_MUTEX; | ||
| 620 | current_thread->wakeup_callback = nullptr; | ||
| 667 | 621 | ||
| 668 | auto wakeup_callback = [mutex, nano_seconds](ThreadWakeupReason reason, | 622 | current_thread->WakeAfterDelay(nano_seconds); |
| 669 | SharedPtr<Thread> thread, | ||
| 670 | SharedPtr<WaitObject> object, size_t index) { | ||
| 671 | ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY); | ||
| 672 | 623 | ||
| 673 | if (reason == ThreadWakeupReason::Timeout) { | 624 | // Note: Deliberately don't attempt to inherit the lock owner's priority. |
| 674 | thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); | ||
| 675 | return true; | ||
| 676 | } | ||
| 677 | 625 | ||
| 678 | ASSERT(reason == ThreadWakeupReason::Signal); | 626 | Core::System::GetInstance().PrepareReschedule(); |
| 627 | return RESULT_SUCCESS; | ||
| 628 | } | ||
| 679 | 629 | ||
| 680 | // Now try to acquire the mutex and don't resume if it's not available. | 630 | /// Signal process wide key |
| 681 | if (!mutex->ShouldWait(thread.get())) { | 631 | static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target) { |
| 682 | mutex->Acquire(thread.get()); | 632 | LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x%llx, target=0x%08x", |
| 683 | thread->SetWaitSynchronizationResult(RESULT_SUCCESS); | 633 | condition_variable_addr, target); |
| 684 | return true; | ||
| 685 | } | ||
| 686 | 634 | ||
| 687 | if (nano_seconds == 0) { | 635 | u32 processed = 0; |
| 688 | thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); | 636 | auto& thread_list = Core::System::GetInstance().Scheduler().GetThreadList(); |
| 689 | return true; | ||
| 690 | } | ||
| 691 | 637 | ||
| 692 | thread->wait_objects = {mutex}; | 638 | for (auto& thread : thread_list) { |
| 693 | mutex->AddWaitingThread(thread); | 639 | if (thread->condvar_wait_address != condition_variable_addr) |
| 694 | thread->status = THREADSTATUS_WAIT_SYNCH_ANY; | 640 | continue; |
| 695 | 641 | ||
| 696 | // Create an event to wake the thread up after the | 642 | // Only process up to 'target' threads, unless 'target' is -1, in which case process |
| 697 | // specified nanosecond delay has passed | 643 | // them all. |
| 698 | thread->WakeAfterDelay(nano_seconds); | 644 | if (target != -1 && processed >= target) |
| 699 | thread->wakeup_callback = DefaultThreadWakeupCallback; | 645 | break; |
| 700 | 646 | ||
| 701 | Core::System::GetInstance().PrepareReschedule(); | 647 | // If the mutex is not yet acquired, acquire it. |
| 648 | u32 mutex_val = Memory::Read32(thread->mutex_wait_address); | ||
| 702 | 649 | ||
| 703 | return false; | 650 | if (mutex_val == 0) { |
| 704 | }; | 651 | // We were able to acquire the mutex, resume this thread. |
| 705 | CASCADE_CODE( | 652 | Memory::Write32(thread->mutex_wait_address, thread->wait_handle); |
| 706 | WaitSynchronization1(condition_variable, thread.get(), nano_seconds, wakeup_callback)); | 653 | ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX); |
| 654 | thread->ResumeFromWait(); | ||
| 707 | 655 | ||
| 708 | return RESULT_SUCCESS; | 656 | auto lock_owner = thread->lock_owner; |
| 709 | } | 657 | if (lock_owner) |
| 658 | lock_owner->RemoveMutexWaiter(thread); | ||
| 710 | 659 | ||
| 711 | /// Signal process wide key | 660 | thread->lock_owner = nullptr; |
| 712 | static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target) { | 661 | thread->mutex_wait_address = 0; |
| 713 | LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x%llx, target=0x%08x", | 662 | thread->condvar_wait_address = 0; |
| 714 | condition_variable_addr, target); | 663 | thread->wait_handle = 0; |
| 664 | } else { | ||
| 665 | // Couldn't acquire the mutex, block the thread. | ||
| 666 | Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); | ||
| 667 | auto owner = g_handle_table.Get<Thread>(owner_handle); | ||
| 668 | ASSERT(owner); | ||
| 669 | ASSERT(thread->status != THREADSTATUS_RUNNING); | ||
| 670 | thread->status = THREADSTATUS_WAIT_MUTEX; | ||
| 671 | thread->wakeup_callback = nullptr; | ||
| 715 | 672 | ||
| 716 | // Wakeup all or one thread - Any other value is unimplemented | 673 | // Signal that the mutex now has a waiting thread. |
| 717 | ASSERT(target == -1 || target == 1); | 674 | Memory::Write32(thread->mutex_wait_address, mutex_val | Mutex::MutexHasWaitersFlag); |
| 718 | 675 | ||
| 719 | SharedPtr<ConditionVariable> condition_variable = | 676 | owner->AddMutexWaiter(thread); |
| 720 | g_object_address_table.Get<ConditionVariable>(condition_variable_addr); | ||
| 721 | if (!condition_variable) { | ||
| 722 | // Create a new condition_variable for the specified address if one does not already exist | ||
| 723 | condition_variable = ConditionVariable::Create(condition_variable_addr).Unwrap(); | ||
| 724 | condition_variable->name = | ||
| 725 | Common::StringFromFormat("condition-variable-%llx", condition_variable_addr); | ||
| 726 | } | ||
| 727 | 677 | ||
| 728 | CASCADE_CODE(condition_variable->Release(target)); | 678 | Core::System::GetInstance().PrepareReschedule(); |
| 679 | } | ||
| 729 | 680 | ||
| 730 | if (condition_variable->mutex_addr) { | 681 | ++processed; |
| 731 | // If a mutex was created for this condition_variable, wait the current thread on it | ||
| 732 | SharedPtr<Mutex> mutex = g_object_address_table.Get<Mutex>(condition_variable->mutex_addr); | ||
| 733 | return WaitSynchronization1(mutex, GetCurrentThread()); | ||
| 734 | } | 682 | } |
| 735 | 683 | ||
| 736 | return RESULT_SUCCESS; | 684 | return RESULT_SUCCESS; |