summaryrefslogtreecommitdiff
path: root/src/core/hle/kernel
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/core/hle/kernel
parentkernel: Add ObjectAddressTable class. (diff)
downloadyuzu-b9950cd4b0cf414ca97daa5f7578f4ca22b2669d.tar.gz
yuzu-b9950cd4b0cf414ca97daa5f7578f4ca22b2669d.tar.xz
yuzu-b9950cd4b0cf414ca97daa5f7578f4ca22b2669d.zip
svc: Implement svcLockMutex.
Diffstat (limited to 'src/core/hle/kernel')
-rw-r--r--src/core/hle/kernel/mutex.cpp81
-rw-r--r--src/core/hle/kernel/mutex.h37
2 files changed, 95 insertions, 23 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