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/mutex.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/mutex.cpp')
| -rw-r--r-- | src/core/hle/kernel/mutex.cpp | 179 |
1 files changed, 88 insertions, 91 deletions
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 0b9dc700c..63733ad79 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <boost/range/algorithm_ext/erase.hpp> | 7 | #include <boost/range/algorithm_ext/erase.hpp> |
| 8 | #include "common/assert.h" | 8 | #include "common/assert.h" |
| 9 | #include "core/core.h" | 9 | #include "core/core.h" |
| 10 | #include "core/hle/kernel/errors.h" | ||
| 10 | #include "core/hle/kernel/handle_table.h" | 11 | #include "core/hle/kernel/handle_table.h" |
| 11 | #include "core/hle/kernel/kernel.h" | 12 | #include "core/hle/kernel/kernel.h" |
| 12 | #include "core/hle/kernel/mutex.h" | 13 | #include "core/hle/kernel/mutex.h" |
| @@ -15,124 +16,120 @@ | |||
| 15 | 16 | ||
| 16 | namespace Kernel { | 17 | namespace Kernel { |
| 17 | 18 | ||
| 18 | void ReleaseThreadMutexes(Thread* thread) { | 19 | /// Returns the number of threads that are waiting for a mutex, and the highest priority one among |
| 19 | for (auto& mtx : thread->held_mutexes) { | 20 | /// those. |
| 20 | mtx->SetHasWaiters(false); | 21 | static std::pair<SharedPtr<Thread>, u32> GetHighestPriorityMutexWaitingThread( |
| 21 | mtx->SetHoldingThread(nullptr); | 22 | SharedPtr<Thread> current_thread, VAddr mutex_addr) { |
| 22 | mtx->WakeupAllWaitingThreads(); | ||
| 23 | } | ||
| 24 | thread->held_mutexes.clear(); | ||
| 25 | } | ||
| 26 | 23 | ||
| 27 | Mutex::Mutex() {} | 24 | SharedPtr<Thread> highest_priority_thread; |
| 28 | Mutex::~Mutex() {} | 25 | u32 num_waiters = 0; |
| 29 | 26 | ||
| 30 | SharedPtr<Mutex> Mutex::Create(SharedPtr<Kernel::Thread> holding_thread, VAddr guest_addr, | 27 | for (auto& thread : current_thread->wait_mutex_threads) { |
| 31 | std::string name) { | 28 | if (thread->mutex_wait_address != mutex_addr) |
| 32 | SharedPtr<Mutex> mutex(new Mutex); | 29 | continue; |
| 33 | 30 | ||
| 34 | mutex->guest_addr = guest_addr; | 31 | ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX); |
| 35 | mutex->name = std::move(name); | ||
| 36 | 32 | ||
| 37 | // If mutex was initialized with a holding thread, acquire it by the holding thread | 33 | ++num_waiters; |
| 38 | if (holding_thread) { | 34 | if (highest_priority_thread == nullptr || |
| 39 | mutex->Acquire(holding_thread.get()); | 35 | thread->GetPriority() < highest_priority_thread->GetPriority()) { |
| 36 | highest_priority_thread = thread; | ||
| 37 | } | ||
| 40 | } | 38 | } |
| 41 | 39 | ||
| 42 | // Mutexes are referenced by guest address, so track this in the kernel | 40 | return {highest_priority_thread, num_waiters}; |
| 43 | g_object_address_table.Insert(guest_addr, mutex); | ||
| 44 | |||
| 45 | return mutex; | ||
| 46 | } | 41 | } |
| 47 | 42 | ||
| 48 | bool Mutex::ShouldWait(Thread* thread) const { | 43 | /// Update the mutex owner field of all threads waiting on the mutex to point to the new owner. |
| 49 | auto holding_thread = GetHoldingThread(); | 44 | static void TransferMutexOwnership(VAddr mutex_addr, SharedPtr<Thread> current_thread, |
| 50 | return holding_thread != nullptr && thread != holding_thread; | 45 | SharedPtr<Thread> new_owner) { |
| 46 | auto threads = current_thread->wait_mutex_threads; | ||
| 47 | for (auto& thread : threads) { | ||
| 48 | if (thread->mutex_wait_address != mutex_addr) | ||
| 49 | continue; | ||
| 50 | |||
| 51 | ASSERT(thread->lock_owner == current_thread); | ||
| 52 | current_thread->RemoveMutexWaiter(thread); | ||
| 53 | if (new_owner != thread) | ||
| 54 | new_owner->AddMutexWaiter(thread); | ||
| 55 | } | ||
| 51 | } | 56 | } |
| 52 | 57 | ||
| 53 | void Mutex::Acquire(Thread* thread) { | 58 | ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle, |
| 54 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); | 59 | Handle requesting_thread_handle) { |
| 60 | // The mutex address must be 4-byte aligned | ||
| 61 | if ((address % sizeof(u32)) != 0) { | ||
| 62 | return ResultCode(ErrorModule::Kernel, ErrCodes::MisalignedAddress); | ||
| 63 | } | ||
| 55 | 64 | ||
| 56 | priority = thread->current_priority; | 65 | SharedPtr<Thread> holding_thread = g_handle_table.Get<Thread>(holding_thread_handle); |
| 57 | thread->held_mutexes.insert(this); | 66 | SharedPtr<Thread> requesting_thread = g_handle_table.Get<Thread>(requesting_thread_handle); |
| 58 | SetHoldingThread(thread); | ||
| 59 | thread->UpdatePriority(); | ||
| 60 | Core::System::GetInstance().PrepareReschedule(); | ||
| 61 | } | ||
| 62 | 67 | ||
| 63 | ResultCode Mutex::Release(Thread* thread) { | 68 | // TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of another |
| 64 | auto holding_thread = GetHoldingThread(); | 69 | // thread. |
| 65 | ASSERT(holding_thread); | 70 | ASSERT(requesting_thread == GetCurrentThread()); |
| 66 | 71 | ||
| 67 | // We can only release the mutex if it's held by the calling thread. | 72 | u32 addr_value = Memory::Read32(address); |
| 68 | ASSERT(thread == holding_thread); | 73 | |
| 74 | // If the mutex isn't being held, just return success. | ||
| 75 | if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) { | ||
| 76 | return RESULT_SUCCESS; | ||
| 77 | } | ||
| 78 | |||
| 79 | if (holding_thread == nullptr) | ||
| 80 | return ERR_INVALID_HANDLE; | ||
| 81 | |||
| 82 | // Wait until the mutex is released | ||
| 83 | GetCurrentThread()->mutex_wait_address = address; | ||
| 84 | GetCurrentThread()->wait_handle = requesting_thread_handle; | ||
| 85 | |||
| 86 | GetCurrentThread()->status = THREADSTATUS_WAIT_MUTEX; | ||
| 87 | GetCurrentThread()->wakeup_callback = nullptr; | ||
| 88 | |||
| 89 | // Update the lock holder thread's priority to prevent priority inversion. | ||
| 90 | holding_thread->AddMutexWaiter(GetCurrentThread()); | ||
| 69 | 91 | ||
| 70 | holding_thread->held_mutexes.erase(this); | ||
| 71 | holding_thread->UpdatePriority(); | ||
| 72 | SetHoldingThread(nullptr); | ||
| 73 | SetHasWaiters(!GetWaitingThreads().empty()); | ||
| 74 | WakeupAllWaitingThreads(); | ||
| 75 | Core::System::GetInstance().PrepareReschedule(); | 92 | Core::System::GetInstance().PrepareReschedule(); |
| 76 | 93 | ||
| 77 | return RESULT_SUCCESS; | 94 | return RESULT_SUCCESS; |
| 78 | } | 95 | } |
| 79 | 96 | ||
| 80 | void Mutex::AddWaitingThread(SharedPtr<Thread> thread) { | 97 | ResultCode Mutex::Release(VAddr address) { |
| 81 | WaitObject::AddWaitingThread(thread); | 98 | // The mutex address must be 4-byte aligned |
| 82 | thread->pending_mutexes.insert(this); | 99 | if ((address % sizeof(u32)) != 0) { |
| 83 | SetHasWaiters(true); | 100 | return ResultCode(ErrorModule::Kernel, ErrCodes::MisalignedAddress); |
| 84 | UpdatePriority(); | 101 | } |
| 85 | } | ||
| 86 | |||
| 87 | void Mutex::RemoveWaitingThread(Thread* thread) { | ||
| 88 | WaitObject::RemoveWaitingThread(thread); | ||
| 89 | thread->pending_mutexes.erase(this); | ||
| 90 | if (!GetHasWaiters()) | ||
| 91 | SetHasWaiters(!GetWaitingThreads().empty()); | ||
| 92 | UpdatePriority(); | ||
| 93 | } | ||
| 94 | 102 | ||
| 95 | void Mutex::UpdatePriority() { | 103 | auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(GetCurrentThread(), address); |
| 96 | if (!GetHoldingThread()) | ||
| 97 | return; | ||
| 98 | 104 | ||
| 99 | u32 best_priority = THREADPRIO_LOWEST; | 105 | // There are no more threads waiting for the mutex, release it completely. |
| 100 | for (auto& waiter : GetWaitingThreads()) { | 106 | if (thread == nullptr) { |
| 101 | if (waiter->current_priority < best_priority) | 107 | ASSERT(GetCurrentThread()->wait_mutex_threads.empty()); |
| 102 | best_priority = waiter->current_priority; | 108 | Memory::Write32(address, 0); |
| 109 | return RESULT_SUCCESS; | ||
| 103 | } | 110 | } |
| 104 | 111 | ||
| 105 | if (best_priority != priority) { | 112 | // Transfer the ownership of the mutex from the previous owner to the new one. |
| 106 | priority = best_priority; | 113 | TransferMutexOwnership(address, GetCurrentThread(), thread); |
| 107 | GetHoldingThread()->UpdatePriority(); | ||
| 108 | } | ||
| 109 | } | ||
| 110 | 114 | ||
| 111 | Handle Mutex::GetOwnerHandle() const { | 115 | u32 mutex_value = thread->wait_handle; |
| 112 | GuestState guest_state{Memory::Read32(guest_addr)}; | ||
| 113 | return guest_state.holding_thread_handle; | ||
| 114 | } | ||
| 115 | 116 | ||
| 116 | SharedPtr<Thread> Mutex::GetHoldingThread() const { | 117 | if (num_waiters >= 2) { |
| 117 | GuestState guest_state{Memory::Read32(guest_addr)}; | 118 | // Notify the guest that there are still some threads waiting for the mutex |
| 118 | return g_handle_table.Get<Thread>(guest_state.holding_thread_handle); | 119 | mutex_value |= Mutex::MutexHasWaitersFlag; |
| 119 | } | 120 | } |
| 120 | 121 | ||
| 121 | void Mutex::SetHoldingThread(SharedPtr<Thread> thread) { | 122 | // Grant the mutex to the next waiting thread and resume it. |
| 122 | GuestState guest_state{Memory::Read32(guest_addr)}; | 123 | Memory::Write32(address, mutex_value); |
| 123 | guest_state.holding_thread_handle.Assign(thread ? thread->guest_handle : 0); | ||
| 124 | Memory::Write32(guest_addr, guest_state.raw); | ||
| 125 | } | ||
| 126 | 124 | ||
| 127 | bool Mutex::GetHasWaiters() const { | 125 | ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX); |
| 128 | GuestState guest_state{Memory::Read32(guest_addr)}; | 126 | thread->ResumeFromWait(); |
| 129 | return guest_state.has_waiters != 0; | ||
| 130 | } | ||
| 131 | 127 | ||
| 132 | void Mutex::SetHasWaiters(bool has_waiters) { | 128 | thread->lock_owner = nullptr; |
| 133 | GuestState guest_state{Memory::Read32(guest_addr)}; | 129 | thread->condvar_wait_address = 0; |
| 134 | guest_state.has_waiters.Assign(has_waiters ? 1 : 0); | 130 | thread->mutex_wait_address = 0; |
| 135 | Memory::Write32(guest_addr, guest_state.raw); | 131 | thread->wait_handle = 0; |
| 136 | } | ||
| 137 | 132 | ||
| 133 | return RESULT_SUCCESS; | ||
| 134 | } | ||
| 138 | } // namespace Kernel | 135 | } // namespace Kernel |