summaryrefslogtreecommitdiff
path: root/src/core/hle/kernel/mutex.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle/kernel/mutex.cpp')
-rw-r--r--src/core/hle/kernel/mutex.cpp170
1 files changed, 0 insertions, 170 deletions
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
deleted file mode 100644
index 4f8075e0e..000000000
--- a/src/core/hle/kernel/mutex.cpp
+++ /dev/null
@@ -1,170 +0,0 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <memory>
6#include <utility>
7#include <vector>
8
9#include "common/assert.h"
10#include "common/logging/log.h"
11#include "core/core.h"
12#include "core/hle/kernel/errors.h"
13#include "core/hle/kernel/handle_table.h"
14#include "core/hle/kernel/k_scheduler.h"
15#include "core/hle/kernel/kernel.h"
16#include "core/hle/kernel/mutex.h"
17#include "core/hle/kernel/object.h"
18#include "core/hle/kernel/process.h"
19#include "core/hle/kernel/thread.h"
20#include "core/hle/result.h"
21#include "core/memory.h"
22
23namespace Kernel {
24
25/// Returns the number of threads that are waiting for a mutex, and the highest priority one among
26/// those.
27static std::pair<std::shared_ptr<Thread>, u32> GetHighestPriorityMutexWaitingThread(
28 const std::shared_ptr<Thread>& current_thread, VAddr mutex_addr) {
29
30 std::shared_ptr<Thread> highest_priority_thread;
31 u32 num_waiters = 0;
32
33 for (const auto& thread : current_thread->GetMutexWaitingThreads()) {
34 if (thread->GetMutexWaitAddress() != mutex_addr)
35 continue;
36
37 ++num_waiters;
38 if (highest_priority_thread == nullptr ||
39 thread->GetPriority() < highest_priority_thread->GetPriority()) {
40 highest_priority_thread = thread;
41 }
42 }
43
44 return {highest_priority_thread, num_waiters};
45}
46
47/// Update the mutex owner field of all threads waiting on the mutex to point to the new owner.
48static void TransferMutexOwnership(VAddr mutex_addr, std::shared_ptr<Thread> current_thread,
49 std::shared_ptr<Thread> new_owner) {
50 current_thread->RemoveMutexWaiter(new_owner);
51 const auto threads = current_thread->GetMutexWaitingThreads();
52 for (const auto& thread : threads) {
53 if (thread->GetMutexWaitAddress() != mutex_addr)
54 continue;
55
56 ASSERT(thread->GetLockOwner() == current_thread.get());
57 current_thread->RemoveMutexWaiter(thread);
58 if (new_owner != thread)
59 new_owner->AddMutexWaiter(thread);
60 }
61}
62
63Mutex::Mutex(Core::System& system) : system{system} {}
64Mutex::~Mutex() = default;
65
66ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
67 Handle requesting_thread_handle) {
68 // The mutex address must be 4-byte aligned
69 if ((address % sizeof(u32)) != 0) {
70 LOG_ERROR(Kernel, "Address is not 4-byte aligned! address={:016X}", address);
71 return ERR_INVALID_ADDRESS;
72 }
73
74 auto& kernel = system.Kernel();
75 std::shared_ptr<Thread> current_thread =
76 SharedFrom(kernel.CurrentScheduler()->GetCurrentThread());
77 {
78 KScopedSchedulerLock lock(kernel);
79 // The mutex address must be 4-byte aligned
80 if ((address % sizeof(u32)) != 0) {
81 return ERR_INVALID_ADDRESS;
82 }
83
84 const auto& handle_table = kernel.CurrentProcess()->GetHandleTable();
85 std::shared_ptr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle);
86 std::shared_ptr<Thread> requesting_thread =
87 handle_table.Get<Thread>(requesting_thread_handle);
88
89 // TODO(Subv): It is currently unknown if it is possible to lock a mutex in behalf of
90 // another thread.
91 ASSERT(requesting_thread == current_thread);
92
93 current_thread->SetSynchronizationResults(nullptr, RESULT_SUCCESS);
94
95 const u32 addr_value = system.Memory().Read32(address);
96
97 // If the mutex isn't being held, just return success.
98 if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) {
99 return RESULT_SUCCESS;
100 }
101
102 if (holding_thread == nullptr) {
103 return ERR_INVALID_HANDLE;
104 }
105
106 // Wait until the mutex is released
107 current_thread->SetMutexWaitAddress(address);
108 current_thread->SetWaitHandle(requesting_thread_handle);
109
110 current_thread->SetStatus(ThreadStatus::WaitMutex);
111
112 // Update the lock holder thread's priority to prevent priority inversion.
113 holding_thread->AddMutexWaiter(current_thread);
114 }
115
116 {
117 KScopedSchedulerLock lock(kernel);
118 auto* owner = current_thread->GetLockOwner();
119 if (owner != nullptr) {
120 owner->RemoveMutexWaiter(current_thread);
121 }
122 }
123 return current_thread->GetSignalingResult();
124}
125
126std::pair<ResultCode, std::shared_ptr<Thread>> Mutex::Unlock(std::shared_ptr<Thread> owner,
127 VAddr address) {
128 // The mutex address must be 4-byte aligned
129 if ((address % sizeof(u32)) != 0) {
130 LOG_ERROR(Kernel, "Address is not 4-byte aligned! address={:016X}", address);
131 return {ERR_INVALID_ADDRESS, nullptr};
132 }
133
134 auto [new_owner, num_waiters] = GetHighestPriorityMutexWaitingThread(owner, address);
135 if (new_owner == nullptr) {
136 system.Memory().Write32(address, 0);
137 return {RESULT_SUCCESS, nullptr};
138 }
139 // Transfer the ownership of the mutex from the previous owner to the new one.
140 TransferMutexOwnership(address, owner, new_owner);
141 u32 mutex_value = new_owner->GetWaitHandle();
142 if (num_waiters >= 2) {
143 // Notify the guest that there are still some threads waiting for the mutex
144 mutex_value |= Mutex::MutexHasWaitersFlag;
145 }
146 new_owner->SetSynchronizationResults(nullptr, RESULT_SUCCESS);
147 new_owner->SetLockOwner(nullptr);
148 new_owner->ResumeFromWait();
149
150 system.Memory().Write32(address, mutex_value);
151 return {RESULT_SUCCESS, new_owner};
152}
153
154ResultCode Mutex::Release(VAddr address) {
155 auto& kernel = system.Kernel();
156 KScopedSchedulerLock lock(kernel);
157
158 std::shared_ptr<Thread> current_thread =
159 SharedFrom(kernel.CurrentScheduler()->GetCurrentThread());
160
161 auto [result, new_owner] = Unlock(current_thread, address);
162
163 if (result != RESULT_SUCCESS && new_owner != nullptr) {
164 new_owner->SetSynchronizationResults(nullptr, result);
165 }
166
167 return result;
168}
169
170} // namespace Kernel