diff options
| author | 2018-01-08 14:12:03 -0500 | |
|---|---|---|
| committer | 2018-01-08 21:12:51 -0500 | |
| commit | 1bbe9309daa96b5fbac7a2df5a2edcb17ab7a05c (patch) | |
| tree | 71a8fc9f6ab552fb242923372238f691caa4e3d6 /src/core/hle/kernel/mutex.cpp | |
| parent | Kernel: Allow chaining WaitSynchronization calls inside a wakeup callback. (diff) | |
| download | yuzu-1bbe9309daa96b5fbac7a2df5a2edcb17ab7a05c.tar.gz yuzu-1bbe9309daa96b5fbac7a2df5a2edcb17ab7a05c.tar.xz yuzu-1bbe9309daa96b5fbac7a2df5a2edcb17ab7a05c.zip | |
Kernel: Properly keep track of mutex lock data in the guest memory. This fixes userland locking/unlocking.
Diffstat (limited to 'src/core/hle/kernel/mutex.cpp')
| -rw-r--r-- | src/core/hle/kernel/mutex.cpp | 97 |
1 files changed, 43 insertions, 54 deletions
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 1b7cda740..0ee92a3e3 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp | |||
| @@ -17,8 +17,8 @@ namespace Kernel { | |||
| 17 | 17 | ||
| 18 | void ReleaseThreadMutexes(Thread* thread) { | 18 | void ReleaseThreadMutexes(Thread* thread) { |
| 19 | for (auto& mtx : thread->held_mutexes) { | 19 | for (auto& mtx : thread->held_mutexes) { |
| 20 | mtx->lock_count = 0; | 20 | mtx->SetHasWaiters(false); |
| 21 | mtx->holding_thread = nullptr; | 21 | mtx->SetHoldingThread(nullptr); |
| 22 | mtx->WakeupAllWaitingThreads(); | 22 | mtx->WakeupAllWaitingThreads(); |
| 23 | } | 23 | } |
| 24 | thread->held_mutexes.clear(); | 24 | thread->held_mutexes.clear(); |
| @@ -31,10 +31,8 @@ SharedPtr<Mutex> Mutex::Create(SharedPtr<Kernel::Thread> holding_thread, VAddr g | |||
| 31 | std::string name) { | 31 | std::string name) { |
| 32 | SharedPtr<Mutex> mutex(new Mutex); | 32 | SharedPtr<Mutex> mutex(new Mutex); |
| 33 | 33 | ||
| 34 | mutex->lock_count = 0; | ||
| 35 | mutex->guest_addr = guest_addr; | 34 | mutex->guest_addr = guest_addr; |
| 36 | mutex->name = std::move(name); | 35 | mutex->name = std::move(name); |
| 37 | mutex->holding_thread = nullptr; | ||
| 38 | 36 | ||
| 39 | // If mutex was initialized with a holding thread, acquire it by the holding thread | 37 | // If mutex was initialized with a holding thread, acquire it by the holding thread |
| 40 | if (holding_thread) { | 38 | if (holding_thread) { |
| @@ -51,56 +49,32 @@ SharedPtr<Mutex> Mutex::Create(SharedPtr<Kernel::Thread> holding_thread, VAddr g | |||
| 51 | } | 49 | } |
| 52 | 50 | ||
| 53 | bool Mutex::ShouldWait(Thread* thread) const { | 51 | bool Mutex::ShouldWait(Thread* thread) const { |
| 54 | return lock_count > 0 && thread != holding_thread; | 52 | auto holding_thread = GetHoldingThread(); |
| 53 | return holding_thread != nullptr && thread != holding_thread; | ||
| 55 | } | 54 | } |
| 56 | 55 | ||
| 57 | void Mutex::Acquire(Thread* thread) { | 56 | void Mutex::Acquire(Thread* thread) { |
| 58 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); | 57 | ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); |
| 59 | 58 | ||
| 60 | // Actually "acquire" the mutex only if we don't already have it | 59 | priority = thread->current_priority; |
| 61 | if (lock_count == 0) { | 60 | thread->held_mutexes.insert(this); |
| 62 | priority = thread->current_priority; | 61 | SetHoldingThread(thread); |
| 63 | thread->held_mutexes.insert(this); | 62 | thread->UpdatePriority(); |
| 64 | holding_thread = thread; | 63 | Core::System::GetInstance().PrepareReschedule(); |
| 65 | thread->UpdatePriority(); | ||
| 66 | UpdateGuestState(); | ||
| 67 | Core::System::GetInstance().PrepareReschedule(); | ||
| 68 | } | ||
| 69 | |||
| 70 | lock_count++; | ||
| 71 | } | 64 | } |
| 72 | 65 | ||
| 73 | ResultCode Mutex::Release(Thread* thread) { | 66 | ResultCode Mutex::Release(Thread* thread) { |
| 74 | // We can only release the mutex if it's held by the calling thread. | 67 | auto holding_thread = GetHoldingThread(); |
| 75 | if (thread != holding_thread) { | 68 | ASSERT(holding_thread); |
| 76 | if (holding_thread) { | ||
| 77 | LOG_ERROR( | ||
| 78 | Kernel, | ||
| 79 | "Tried to release a mutex (owned by thread id %u) from a different thread id %u", | ||
| 80 | holding_thread->thread_id, thread->thread_id); | ||
| 81 | } | ||
| 82 | // TODO(bunnei): Use correct error code | ||
| 83 | return ResultCode(-1); | ||
| 84 | } | ||
| 85 | 69 | ||
| 86 | // Note: It should not be possible for the situation where the mutex has a holding thread with a | 70 | // We can only release the mutex if it's held by the calling thread. |
| 87 | // zero lock count to occur. The real kernel still checks for this, so we do too. | 71 | ASSERT(thread == holding_thread); |
| 88 | if (lock_count <= 0) { | ||
| 89 | // TODO(bunnei): Use correct error code | ||
| 90 | return ResultCode(-1); | ||
| 91 | } | ||
| 92 | |||
| 93 | lock_count--; | ||
| 94 | 72 | ||
| 95 | // Yield to the next thread only if we've fully released the mutex | 73 | holding_thread->held_mutexes.erase(this); |
| 96 | if (lock_count == 0) { | 74 | holding_thread->UpdatePriority(); |
| 97 | holding_thread->held_mutexes.erase(this); | 75 | SetHoldingThread(nullptr); |
| 98 | holding_thread->UpdatePriority(); | 76 | WakeupAllWaitingThreads(); |
| 99 | holding_thread = nullptr; | 77 | Core::System::GetInstance().PrepareReschedule(); |
| 100 | WakeupAllWaitingThreads(); | ||
| 101 | UpdateGuestState(); | ||
| 102 | Core::System::GetInstance().PrepareReschedule(); | ||
| 103 | } | ||
| 104 | 78 | ||
| 105 | return RESULT_SUCCESS; | 79 | return RESULT_SUCCESS; |
| 106 | } | 80 | } |
| @@ -108,19 +82,20 @@ ResultCode Mutex::Release(Thread* thread) { | |||
| 108 | void Mutex::AddWaitingThread(SharedPtr<Thread> thread) { | 82 | void Mutex::AddWaitingThread(SharedPtr<Thread> thread) { |
| 109 | WaitObject::AddWaitingThread(thread); | 83 | WaitObject::AddWaitingThread(thread); |
| 110 | thread->pending_mutexes.insert(this); | 84 | thread->pending_mutexes.insert(this); |
| 85 | SetHasWaiters(true); | ||
| 111 | UpdatePriority(); | 86 | UpdatePriority(); |
| 112 | UpdateGuestState(); | ||
| 113 | } | 87 | } |
| 114 | 88 | ||
| 115 | void Mutex::RemoveWaitingThread(Thread* thread) { | 89 | void Mutex::RemoveWaitingThread(Thread* thread) { |
| 116 | WaitObject::RemoveWaitingThread(thread); | 90 | WaitObject::RemoveWaitingThread(thread); |
| 117 | thread->pending_mutexes.erase(this); | 91 | thread->pending_mutexes.erase(this); |
| 92 | if (!GetHasWaiters()) | ||
| 93 | SetHasWaiters(!GetWaitingThreads().empty()); | ||
| 118 | UpdatePriority(); | 94 | UpdatePriority(); |
| 119 | UpdateGuestState(); | ||
| 120 | } | 95 | } |
| 121 | 96 | ||
| 122 | void Mutex::UpdatePriority() { | 97 | void Mutex::UpdatePriority() { |
| 123 | if (!holding_thread) | 98 | if (!GetHoldingThread()) |
| 124 | return; | 99 | return; |
| 125 | 100 | ||
| 126 | u32 best_priority = THREADPRIO_LOWEST; | 101 | u32 best_priority = THREADPRIO_LOWEST; |
| @@ -131,21 +106,35 @@ void Mutex::UpdatePriority() { | |||
| 131 | 106 | ||
| 132 | if (best_priority != priority) { | 107 | if (best_priority != priority) { |
| 133 | priority = best_priority; | 108 | priority = best_priority; |
| 134 | holding_thread->UpdatePriority(); | 109 | GetHoldingThread()->UpdatePriority(); |
| 135 | } | 110 | } |
| 136 | } | 111 | } |
| 137 | 112 | ||
| 138 | void Mutex::UpdateGuestState() { | 113 | Handle Mutex::GetOwnerHandle() const { |
| 114 | GuestState guest_state{Memory::Read32(guest_addr)}; | ||
| 115 | return guest_state.holding_thread_handle; | ||
| 116 | } | ||
| 117 | |||
| 118 | SharedPtr<Thread> Mutex::GetHoldingThread() const { | ||
| 119 | GuestState guest_state{Memory::Read32(guest_addr)}; | ||
| 120 | return g_handle_table.Get<Thread>(guest_state.holding_thread_handle); | ||
| 121 | } | ||
| 122 | |||
| 123 | void Mutex::SetHoldingThread(SharedPtr<Thread> thread) { | ||
| 139 | GuestState guest_state{Memory::Read32(guest_addr)}; | 124 | GuestState guest_state{Memory::Read32(guest_addr)}; |
| 140 | guest_state.has_waiters.Assign(!GetWaitingThreads().empty()); | 125 | guest_state.holding_thread_handle.Assign(thread ? thread->guest_handle : 0); |
| 141 | guest_state.holding_thread_handle.Assign(holding_thread ? holding_thread->guest_handle : 0); | ||
| 142 | Memory::Write32(guest_addr, guest_state.raw); | 126 | Memory::Write32(guest_addr, guest_state.raw); |
| 143 | } | 127 | } |
| 144 | 128 | ||
| 145 | void Mutex::VerifyGuestState() { | 129 | bool Mutex::GetHasWaiters() const { |
| 146 | GuestState guest_state{Memory::Read32(guest_addr)}; | 130 | GuestState guest_state{Memory::Read32(guest_addr)}; |
| 147 | ASSERT(guest_state.has_waiters == !GetWaitingThreads().empty()); | 131 | return guest_state.has_waiters != 0; |
| 148 | ASSERT(guest_state.holding_thread_handle == holding_thread->guest_handle); | 132 | } |
| 133 | |||
| 134 | void Mutex::SetHasWaiters(bool has_waiters) { | ||
| 135 | GuestState guest_state{Memory::Read32(guest_addr)}; | ||
| 136 | guest_state.has_waiters.Assign(has_waiters ? 1 : 0); | ||
| 137 | Memory::Write32(guest_addr, guest_state.raw); | ||
| 149 | } | 138 | } |
| 150 | 139 | ||
| 151 | } // namespace Kernel | 140 | } // namespace Kernel |