summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar bunnei2018-01-01 14:02:26 -0500
committerGravatar bunnei2018-01-01 14:02:26 -0500
commitb9950cd4b0cf414ca97daa5f7578f4ca22b2669d (patch)
tree194c711f7f02e68e5137f3f7287dbd21ab834d5b /src
parentkernel: Add ObjectAddressTable class. (diff)
downloadyuzu-b9950cd4b0cf414ca97daa5f7578f4ca22b2669d.tar.gz
yuzu-b9950cd4b0cf414ca97daa5f7578f4ca22b2669d.tar.xz
yuzu-b9950cd4b0cf414ca97daa5f7578f4ca22b2669d.zip
svc: Implement svcLockMutex.
Diffstat (limited to 'src')
-rw-r--r--src/core/hle/kernel/mutex.cpp81
-rw-r--r--src/core/hle/kernel/mutex.h37
-rw-r--r--src/core/hle/svc.cpp40
3 files changed, 134 insertions, 24 deletions
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index 30dade552..1b7cda740 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -7,8 +7,10 @@
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/handle_table.h"
10#include "core/hle/kernel/kernel.h" 11#include "core/hle/kernel/kernel.h"
11#include "core/hle/kernel/mutex.h" 12#include "core/hle/kernel/mutex.h"
13#include "core/hle/kernel/object_address_table.h"
12#include "core/hle/kernel/thread.h" 14#include "core/hle/kernel/thread.h"
13 15
14namespace Kernel { 16namespace Kernel {
@@ -25,17 +27,25 @@ void ReleaseThreadMutexes(Thread* thread) {
25Mutex::Mutex() {} 27Mutex::Mutex() {}
26Mutex::~Mutex() {} 28Mutex::~Mutex() {}
27 29
28SharedPtr<Mutex> Mutex::Create(bool initial_locked, VAddr addr, std::string name) { 30SharedPtr<Mutex> Mutex::Create(SharedPtr<Kernel::Thread> holding_thread, VAddr guest_addr,
31 std::string name) {
29 SharedPtr<Mutex> mutex(new Mutex); 32 SharedPtr<Mutex> mutex(new Mutex);
30 33
31 mutex->lock_count = 0; 34 mutex->lock_count = 0;
32 mutex->addr = addr; 35 mutex->guest_addr = guest_addr;
33 mutex->name = std::move(name); 36 mutex->name = std::move(name);
34 mutex->holding_thread = nullptr; 37 mutex->holding_thread = nullptr;
35 38
36 // Acquire mutex with current thread if initialized as locked 39 // If mutex was initialized with a holding thread, acquire it by the holding thread
37 if (initial_locked) 40 if (holding_thread) {
38 mutex->Acquire(GetCurrentThread()); 41 mutex->Acquire(holding_thread.get());
42 }
43
44 // Mutexes are referenced by guest address, so track this in the kernel
45 g_object_address_table.Insert(guest_addr, mutex);
46
47 // Verify that the created mutex matches the guest state for the mutex
48 mutex->VerifyGuestState();
39 49
40 return mutex; 50 return mutex;
41} 51}
@@ -53,38 +63,60 @@ void Mutex::Acquire(Thread* thread) {
53 thread->held_mutexes.insert(this); 63 thread->held_mutexes.insert(this);
54 holding_thread = thread; 64 holding_thread = thread;
55 thread->UpdatePriority(); 65 thread->UpdatePriority();
66 UpdateGuestState();
56 Core::System::GetInstance().PrepareReschedule(); 67 Core::System::GetInstance().PrepareReschedule();
57 } 68 }
58 69
59 lock_count++; 70 lock_count++;
60} 71}
61 72
62void Mutex::Release() { 73ResultCode Mutex::Release(Thread* thread) {
63 // Only release if the mutex is held 74 // We can only release the mutex if it's held by the calling thread.
64 if (lock_count > 0) { 75 if (thread != holding_thread) {
65 lock_count--; 76 if (holding_thread) {
66 77 LOG_ERROR(
67 // Yield to the next thread only if we've fully released the mutex 78 Kernel,
68 if (lock_count == 0) { 79 "Tried to release a mutex (owned by thread id %u) from a different thread id %u",
69 holding_thread->held_mutexes.erase(this); 80 holding_thread->thread_id, thread->thread_id);
70 holding_thread->UpdatePriority();
71 holding_thread = nullptr;
72 WakeupAllWaitingThreads();
73 Core::System::GetInstance().PrepareReschedule();
74 } 81 }
82 // TODO(bunnei): Use correct error code
83 return ResultCode(-1);
84 }
85
86 // Note: It should not be possible for the situation where the mutex has a holding thread with a
87 // zero lock count to occur. The real kernel still checks for this, so we do too.
88 if (lock_count <= 0) {
89 // TODO(bunnei): Use correct error code
90 return ResultCode(-1);
91 }
92
93 lock_count--;
94
95 // Yield to the next thread only if we've fully released the mutex
96 if (lock_count == 0) {
97 holding_thread->held_mutexes.erase(this);
98 holding_thread->UpdatePriority();
99 holding_thread = nullptr;
100 WakeupAllWaitingThreads();
101 UpdateGuestState();
102 Core::System::GetInstance().PrepareReschedule();
75 } 103 }
104
105 return RESULT_SUCCESS;
76} 106}
77 107
78void Mutex::AddWaitingThread(SharedPtr<Thread> thread) { 108void Mutex::AddWaitingThread(SharedPtr<Thread> thread) {
79 WaitObject::AddWaitingThread(thread); 109 WaitObject::AddWaitingThread(thread);
80 thread->pending_mutexes.insert(this); 110 thread->pending_mutexes.insert(this);
81 UpdatePriority(); 111 UpdatePriority();
112 UpdateGuestState();
82} 113}
83 114
84void Mutex::RemoveWaitingThread(Thread* thread) { 115void Mutex::RemoveWaitingThread(Thread* thread) {
85 WaitObject::RemoveWaitingThread(thread); 116 WaitObject::RemoveWaitingThread(thread);
86 thread->pending_mutexes.erase(this); 117 thread->pending_mutexes.erase(this);
87 UpdatePriority(); 118 UpdatePriority();
119 UpdateGuestState();
88} 120}
89 121
90void Mutex::UpdatePriority() { 122void Mutex::UpdatePriority() {
@@ -103,4 +135,17 @@ void Mutex::UpdatePriority() {
103 } 135 }
104} 136}
105 137
106} // namespace 138void Mutex::UpdateGuestState() {
139 GuestState guest_state{Memory::Read32(guest_addr)};
140 guest_state.has_waiters.Assign(!GetWaitingThreads().empty());
141 guest_state.holding_thread_handle.Assign(holding_thread ? holding_thread->guest_handle : 0);
142 Memory::Write32(guest_addr, guest_state.raw);
143}
144
145void Mutex::VerifyGuestState() {
146 GuestState guest_state{Memory::Read32(guest_addr)};
147 ASSERT(guest_state.has_waiters == !GetWaitingThreads().empty());
148 ASSERT(guest_state.holding_thread_handle == holding_thread->guest_handle);
149}
150
151} // namespace Kernel
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h
index 503d3ee75..87e3c15ee 100644
--- a/src/core/hle/kernel/mutex.h
+++ b/src/core/hle/kernel/mutex.h
@@ -6,8 +6,10 @@
6 6
7#include <string> 7#include <string>
8#include "common/common_types.h" 8#include "common/common_types.h"
9#include "common/swap.h"
9#include "core/hle/kernel/kernel.h" 10#include "core/hle/kernel/kernel.h"
10#include "core/hle/kernel/wait_object.h" 11#include "core/hle/kernel/wait_object.h"
12#include "core/hle/result.h"
11 13
12namespace Kernel { 14namespace Kernel {
13 15
@@ -17,11 +19,15 @@ class Mutex final : public WaitObject {
17public: 19public:
18 /** 20 /**
19 * Creates a mutex. 21 * Creates a mutex.
20 * @param initial_locked Specifies if the mutex should be locked initially 22 * @param holding_thread Specifies a thread already holding the mutex. If not nullptr, this
23 * thread will acquire the mutex.
24 * @param guest_addr Address of the object tracking the mutex in guest memory. If specified,
25 * this mutex will update the guest object when its state changes.
21 * @param name Optional name of mutex 26 * @param name Optional name of mutex
22 * @return Pointer to new Mutex object 27 * @return Pointer to new Mutex object
23 */ 28 */
24 static SharedPtr<Mutex> Create(bool initial_locked, VAddr addr, std::string name = "Unknown"); 29 static SharedPtr<Mutex> Create(SharedPtr<Kernel::Thread> holding_thread, VAddr guest_addr = 0,
30 std::string name = "Unknown");
25 31
26 std::string GetTypeName() const override { 32 std::string GetTypeName() const override {
27 return "Mutex"; 33 return "Mutex";
@@ -39,7 +45,7 @@ public:
39 u32 priority; ///< The priority of the mutex, used for priority inheritance. 45 u32 priority; ///< The priority of the mutex, used for priority inheritance.
40 std::string name; ///< Name of mutex (optional) 46 std::string name; ///< Name of mutex (optional)
41 SharedPtr<Thread> holding_thread; ///< Thread that has acquired the mutex 47 SharedPtr<Thread> holding_thread; ///< Thread that has acquired the mutex
42 VAddr addr; 48 VAddr guest_addr; ///< Address of the guest mutex value
43 49
44 /** 50 /**
45 * Elevate the mutex priority to the best priority 51 * Elevate the mutex priority to the best priority
@@ -53,11 +59,32 @@ public:
53 void AddWaitingThread(SharedPtr<Thread> thread) override; 59 void AddWaitingThread(SharedPtr<Thread> thread) override;
54 void RemoveWaitingThread(Thread* thread) override; 60 void RemoveWaitingThread(Thread* thread) override;
55 61
56 void Release(); 62 /**
63 * Attempts to release the mutex from the specified thread.
64 * @param thread Thread that wants to release the mutex.
65 * @returns The result code of the operation.
66 */
67 ResultCode Release(Thread* thread);
57 68
58private: 69private:
59 Mutex(); 70 Mutex();
60 ~Mutex() override; 71 ~Mutex() override;
72
73 /// Object in guest memory used to track the mutex state
74 union GuestState {
75 u32_le raw;
76 /// Handle of the thread that currently holds the mutex, 0 if available
77 BitField<0, 30, u32_le> holding_thread_handle;
78 /// 1 when there are threads waiting for this mutex, otherwise 0
79 BitField<30, 1, u32_le> has_waiters;
80 };
81 static_assert(sizeof(GuestState) == 4, "GuestState size is incorrect");
82
83 /// Updates the state of the object tracking this mutex in guest memory
84 void UpdateGuestState();
85
86 /// Verifies the state of the object tracking this mutex in guest memory
87 void VerifyGuestState();
61}; 88};
62 89
63/** 90/**
@@ -66,4 +93,4 @@ private:
66 */ 93 */
67void ReleaseThreadMutexes(Thread* thread); 94void ReleaseThreadMutexes(Thread* thread);
68 95
69} // namespace 96} // namespace Kernel
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index 133b868f2..c0e3200d4 100644
--- a/src/core/hle/svc.cpp
+++ b/src/core/hle/svc.cpp
@@ -122,6 +122,44 @@ static ResultCode GetProcessId(u32* process_id, Kernel::Handle process_handle) {
122 return RESULT_SUCCESS; 122 return RESULT_SUCCESS;
123} 123}
124 124
125/// Attempts to locks a mutex, creating it if it does not already exist
126static ResultCode LockMutex(Handle holding_thread_handle, VAddr mutex_addr,
127 Handle requesting_thread_handle) {
128 LOG_TRACE(Kernel_SVC,
129 "called holding_thread_handle=0x%08X, mutex_addr=0x%llx, "
130 "requesting_current_thread_handle=0x%08X",
131 holding_thread_handle, mutex_addr, requesting_thread_handle);
132
133 SharedPtr<Kernel::Thread> holding_thread =
134 Kernel::g_handle_table.Get<Kernel::Thread>(holding_thread_handle);
135 SharedPtr<Kernel::Thread> requesting_thread =
136 Kernel::g_handle_table.Get<Kernel::Thread>(requesting_thread_handle);
137
138 ASSERT(holding_thread);
139 ASSERT(requesting_thread);
140
141 SharedPtr<Kernel::Mutex> mutex = Kernel::g_object_address_table.Get<Kernel::Mutex>(mutex_addr);
142 if (!mutex) {
143 // Create a new mutex for the specified address if one does not already exist
144 mutex = Kernel::Mutex::Create(holding_thread, mutex_addr);
145 mutex->name = Common::StringFromFormat("mutex-%llx", mutex_addr);
146 }
147
148 if (mutex->ShouldWait(requesting_thread.get())) {
149 // If we cannot lock the mutex, then put the thread too sleep and trigger a reschedule
150 requesting_thread->wait_objects = {mutex};
151 mutex->AddWaitingThread(requesting_thread);
152 requesting_thread->status = THREADSTATUS_WAIT_SYNCH_ANY;
153
154 Core::System::GetInstance().PrepareReschedule();
155 } else {
156 // The mutex is available, lock it
157 mutex->Acquire(requesting_thread.get());
158 }
159
160 return RESULT_SUCCESS;
161}
162
125/// Break program execution 163/// Break program execution
126static void Break(u64 unk_0, u64 unk_1, u64 unk_2) { 164static void Break(u64 unk_0, u64 unk_1, u64 unk_2) {
127 LOG_CRITICAL(Debug_Emulated, "Emulated program broke execution!"); 165 LOG_CRITICAL(Debug_Emulated, "Emulated program broke execution!");
@@ -371,8 +409,8 @@ static const FunctionDef SVC_Table[] = {
371 {0x17, nullptr, "svcResetSignal"}, 409 {0x17, nullptr, "svcResetSignal"},
372 {0x18, nullptr, "svcWaitSynchronization"}, 410 {0x18, nullptr, "svcWaitSynchronization"},
373 {0x19, nullptr, "svcCancelSynchronization"}, 411 {0x19, nullptr, "svcCancelSynchronization"},
374 {0x1A, nullptr, "svcLockMutex"},
375 {0x1B, nullptr, "svcUnlockMutex"}, 412 {0x1B, nullptr, "svcUnlockMutex"},
413 {0x1A, HLE::Wrap<LockMutex>, "svcLockMutex"},
376 {0x1C, nullptr, "svcWaitProcessWideKeyAtomic"}, 414 {0x1C, nullptr, "svcWaitProcessWideKeyAtomic"},
377 {0x1D, HLE::Wrap<SignalProcessWideKey>, "svcSignalProcessWideKey"}, 415 {0x1D, HLE::Wrap<SignalProcessWideKey>, "svcSignalProcessWideKey"},
378 {0x1E, nullptr, "svcGetSystemTick"}, 416 {0x1E, nullptr, "svcGetSystemTick"},