summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/hle/kernel/condition_variable.cpp64
-rw-r--r--src/core/hle/kernel/condition_variable.h63
-rw-r--r--src/core/hle/kernel/errors.h1
-rw-r--r--src/core/hle/kernel/kernel.h4
-rw-r--r--src/core/hle/kernel/mutex.cpp179
-rw-r--r--src/core/hle/kernel/mutex.h88
-rw-r--r--src/core/hle/kernel/svc.cpp162
-rw-r--r--src/core/hle/kernel/thread.cpp66
-rw-r--r--src/core/hle/kernel/thread.h36
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp6
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h6
-rw-r--r--src/core/loader/nro.cpp2
-rw-r--r--src/core/loader/nso.cpp2
-rw-r--r--src/yuzu/debugger/wait_tree.cpp86
-rw-r--r--src/yuzu/debugger/wait_tree.h43
16 files changed, 285 insertions, 525 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index c1a645460..b3807c204 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -42,8 +42,6 @@ add_library(core STATIC
42 hle/kernel/client_port.h 42 hle/kernel/client_port.h
43 hle/kernel/client_session.cpp 43 hle/kernel/client_session.cpp
44 hle/kernel/client_session.h 44 hle/kernel/client_session.h
45 hle/kernel/condition_variable.cpp
46 hle/kernel/condition_variable.h
47 hle/kernel/errors.h 45 hle/kernel/errors.h
48 hle/kernel/event.cpp 46 hle/kernel/event.cpp
49 hle/kernel/event.h 47 hle/kernel/event.h
diff --git a/src/core/hle/kernel/condition_variable.cpp b/src/core/hle/kernel/condition_variable.cpp
deleted file mode 100644
index a786d7f74..000000000
--- a/src/core/hle/kernel/condition_variable.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "core/hle/kernel/condition_variable.h"
7#include "core/hle/kernel/errors.h"
8#include "core/hle/kernel/kernel.h"
9#include "core/hle/kernel/object_address_table.h"
10#include "core/hle/kernel/thread.h"
11
12namespace Kernel {
13
14ConditionVariable::ConditionVariable() {}
15ConditionVariable::~ConditionVariable() {}
16
17ResultVal<SharedPtr<ConditionVariable>> ConditionVariable::Create(VAddr guest_addr,
18 std::string name) {
19 SharedPtr<ConditionVariable> condition_variable(new ConditionVariable);
20
21 condition_variable->name = std::move(name);
22 condition_variable->guest_addr = guest_addr;
23 condition_variable->mutex_addr = 0;
24
25 // Condition variables are referenced by guest address, so track this in the kernel
26 g_object_address_table.Insert(guest_addr, condition_variable);
27
28 return MakeResult<SharedPtr<ConditionVariable>>(std::move(condition_variable));
29}
30
31bool ConditionVariable::ShouldWait(Thread* thread) const {
32 return GetAvailableCount() <= 0;
33}
34
35void ConditionVariable::Acquire(Thread* thread) {
36 if (GetAvailableCount() <= 0)
37 return;
38
39 SetAvailableCount(GetAvailableCount() - 1);
40}
41
42ResultCode ConditionVariable::Release(s32 target) {
43 if (target == -1) {
44 // When -1, wake up all waiting threads
45 SetAvailableCount(static_cast<s32>(GetWaitingThreads().size()));
46 WakeupAllWaitingThreads();
47 } else {
48 // Otherwise, wake up just a single thread
49 SetAvailableCount(target);
50 WakeupWaitingThread(GetHighestPriorityReadyThread());
51 }
52
53 return RESULT_SUCCESS;
54}
55
56s32 ConditionVariable::GetAvailableCount() const {
57 return Memory::Read32(guest_addr);
58}
59
60void ConditionVariable::SetAvailableCount(s32 value) const {
61 Memory::Write32(guest_addr, value);
62}
63
64} // namespace Kernel
diff --git a/src/core/hle/kernel/condition_variable.h b/src/core/hle/kernel/condition_variable.h
deleted file mode 100644
index 1c9f06769..000000000
--- a/src/core/hle/kernel/condition_variable.h
+++ /dev/null
@@ -1,63 +0,0 @@
1// Copyright 2018 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <string>
8#include <queue>
9#include "common/common_types.h"
10#include "core/hle/kernel/kernel.h"
11#include "core/hle/kernel/wait_object.h"
12#include "core/hle/result.h"
13
14namespace Kernel {
15
16class ConditionVariable final : public WaitObject {
17public:
18 /**
19 * Creates a condition variable.
20 * @param guest_addr Address of the object tracking the condition variable in guest memory. If
21 * specified, this condition variable will update the guest object when its state changes.
22 * @param name Optional name of condition variable.
23 * @return The created condition variable.
24 */
25 static ResultVal<SharedPtr<ConditionVariable>> Create(VAddr guest_addr,
26 std::string name = "Unknown");
27
28 std::string GetTypeName() const override {
29 return "ConditionVariable";
30 }
31 std::string GetName() const override {
32 return name;
33 }
34
35 static const HandleType HANDLE_TYPE = HandleType::ConditionVariable;
36 HandleType GetHandleType() const override {
37 return HANDLE_TYPE;
38 }
39
40 s32 GetAvailableCount() const;
41 void SetAvailableCount(s32 value) const;
42
43 std::string name; ///< Name of condition variable (optional)
44 VAddr guest_addr; ///< Address of the guest condition variable value
45 VAddr mutex_addr; ///< (optional) Address of guest mutex value associated with this condition
46 ///< variable, used for implementing events
47
48 bool ShouldWait(Thread* thread) const override;
49 void Acquire(Thread* thread) override;
50
51 /**
52 * Releases a slot from a condition variable.
53 * @param target The number of threads to wakeup, -1 is all.
54 * @return ResultCode indicating if the operation succeeded.
55 */
56 ResultCode Release(s32 target);
57
58private:
59 ConditionVariable();
60 ~ConditionVariable() override;
61};
62
63} // namespace Kernel
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h
index 29d8dfdaa..5be20c878 100644
--- a/src/core/hle/kernel/errors.h
+++ b/src/core/hle/kernel/errors.h
@@ -20,6 +20,7 @@ enum {
20 MaxConnectionsReached = 52, 20 MaxConnectionsReached = 52,
21 21
22 // Confirmed Switch OS error codes 22 // Confirmed Switch OS error codes
23 MisalignedAddress = 102,
23 InvalidHandle = 114, 24 InvalidHandle = 114,
24 Timeout = 117, 25 Timeout = 117,
25 SynchronizationCanceled = 118, 26 SynchronizationCanceled = 118,
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 053bf4e17..402ae900f 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -18,12 +18,10 @@ using Handle = u32;
18enum class HandleType : u32 { 18enum class HandleType : u32 {
19 Unknown, 19 Unknown,
20 Event, 20 Event,
21 Mutex,
22 SharedMemory, 21 SharedMemory,
23 Thread, 22 Thread,
24 Process, 23 Process,
25 AddressArbiter, 24 AddressArbiter,
26 ConditionVariable,
27 Timer, 25 Timer,
28 ResourceLimit, 26 ResourceLimit,
29 CodeSet, 27 CodeSet,
@@ -63,9 +61,7 @@ public:
63 bool IsWaitable() const { 61 bool IsWaitable() const {
64 switch (GetHandleType()) { 62 switch (GetHandleType()) {
65 case HandleType::Event: 63 case HandleType::Event:
66 case HandleType::Mutex:
67 case HandleType::Thread: 64 case HandleType::Thread:
68 case HandleType::ConditionVariable:
69 case HandleType::Timer: 65 case HandleType::Timer:
70 case HandleType::ServerPort: 66 case HandleType::ServerPort:
71 case HandleType::ServerSession: 67 case HandleType::ServerSession:
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
16namespace Kernel { 17namespace Kernel {
17 18
18void 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); 21static 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
27Mutex::Mutex() {} 24 SharedPtr<Thread> highest_priority_thread;
28Mutex::~Mutex() {} 25 u32 num_waiters = 0;
29 26
30SharedPtr<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
48bool 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(); 44static 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
53void Mutex::Acquire(Thread* thread) { 58ResultCode 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
63ResultCode 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
80void Mutex::AddWaitingThread(SharedPtr<Thread> thread) { 97ResultCode 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
87void 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
95void 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
111Handle 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
116SharedPtr<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
121void 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
127bool 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
132void 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
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h
index 38db21005..3117e7c70 100644
--- a/src/core/hle/kernel/mutex.h
+++ b/src/core/hle/kernel/mutex.h
@@ -15,87 +15,23 @@ namespace Kernel {
15 15
16class Thread; 16class Thread;
17 17
18class Mutex final : public WaitObject { 18class Mutex final {
19public: 19public:
20 /** 20 /// Flag that indicates that a mutex still has threads waiting for it.
21 * Creates a mutex. 21 static constexpr u32 MutexHasWaitersFlag = 0x40000000;
22 * @param holding_thread Specifies a thread already holding the mutex. If not nullptr, this 22 /// Mask of the bits in a mutex address value that contain the mutex owner.
23 * thread will acquire the mutex. 23 static constexpr u32 MutexOwnerMask = 0xBFFFFFFF;
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.
26 * @param name Optional name of mutex
27 * @return Pointer to new Mutex object
28 */
29 static SharedPtr<Mutex> Create(SharedPtr<Kernel::Thread> holding_thread, VAddr guest_addr = 0,
30 std::string name = "Unknown");
31 24
32 std::string GetTypeName() const override { 25 /// Attempts to acquire a mutex at the specified address.
33 return "Mutex"; 26 static ResultCode TryAcquire(VAddr address, Handle holding_thread_handle,
34 } 27 Handle requesting_thread_handle);
35 std::string GetName() const override {
36 return name;
37 }
38 28
39 static const HandleType HANDLE_TYPE = HandleType::Mutex; 29 /// Releases the mutex at the specified address.
40 HandleType GetHandleType() const override { 30 static ResultCode Release(VAddr address);
41 return HANDLE_TYPE;
42 }
43
44 u32 priority; ///< The priority of the mutex, used for priority inheritance.
45 std::string name; ///< Name of mutex (optional)
46 VAddr guest_addr; ///< Address of the guest mutex value
47
48 /**
49 * Elevate the mutex priority to the best priority
50 * among the priorities of all its waiting threads.
51 */
52 void UpdatePriority();
53
54 bool ShouldWait(Thread* thread) const override;
55 void Acquire(Thread* thread) override;
56
57 void AddWaitingThread(SharedPtr<Thread> thread) override;
58 void RemoveWaitingThread(Thread* thread) override;
59
60 /**
61 * Attempts to release the mutex from the specified thread.
62 * @param thread Thread that wants to release the mutex.
63 * @returns The result code of the operation.
64 */
65 ResultCode Release(Thread* thread);
66
67 /// Gets the handle to the holding process stored in the guest state.
68 Handle GetOwnerHandle() const;
69
70 /// Gets the Thread pointed to by the owner handle
71 SharedPtr<Thread> GetHoldingThread() const;
72 /// Sets the holding process handle in the guest state.
73 void SetHoldingThread(SharedPtr<Thread> thread);
74
75 /// Returns the has_waiters bit in the guest state.
76 bool GetHasWaiters() const;
77 /// Sets the has_waiters bit in the guest state.
78 void SetHasWaiters(bool has_waiters);
79 31
80private: 32private:
81 Mutex(); 33 Mutex() = default;
82 ~Mutex() override; 34 ~Mutex() = default;
83
84 /// Object in guest memory used to track the mutex state
85 union GuestState {
86 u32_le raw;
87 /// Handle of the thread that currently holds the mutex, 0 if available
88 BitField<0, 30, u32_le> holding_thread_handle;
89 /// 1 when there are threads waiting for this mutex, otherwise 0
90 BitField<30, 1, u32_le> has_waiters;
91 };
92 static_assert(sizeof(GuestState) == 4, "GuestState size is incorrect");
93}; 35};
94 36
95/**
96 * Releases all the mutexes held by the specified thread
97 * @param thread Thread that is holding the mutexes
98 */
99void ReleaseThreadMutexes(Thread* thread);
100
101} // namespace Kernel 37} // namespace Kernel
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 633740992..c22da6e47 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -13,7 +13,6 @@
13#include "core/core_timing.h" 13#include "core/core_timing.h"
14#include "core/hle/kernel/client_port.h" 14#include "core/hle/kernel/client_port.h"
15#include "core/hle/kernel/client_session.h" 15#include "core/hle/kernel/client_session.h"
16#include "core/hle/kernel/condition_variable.h"
17#include "core/hle/kernel/event.h" 16#include "core/hle/kernel/event.h"
18#include "core/hle/kernel/handle_table.h" 17#include "core/hle/kernel/handle_table.h"
19#include "core/hle/kernel/mutex.h" 18#include "core/hle/kernel/mutex.h"
@@ -262,32 +261,14 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr,
262 "requesting_current_thread_handle=0x%08X", 261 "requesting_current_thread_handle=0x%08X",
263 holding_thread_handle, mutex_addr, requesting_thread_handle); 262 holding_thread_handle, mutex_addr, requesting_thread_handle);
264 263
265 SharedPtr<Thread> holding_thread = g_handle_table.Get<Thread>(holding_thread_handle); 264 return Mutex::TryAcquire(mutex_addr, holding_thread_handle, requesting_thread_handle);
266 SharedPtr<Thread> requesting_thread = g_handle_table.Get<Thread>(requesting_thread_handle);
267
268 ASSERT(requesting_thread);
269 ASSERT(requesting_thread == GetCurrentThread());
270
271 SharedPtr<Mutex> mutex = g_object_address_table.Get<Mutex>(mutex_addr);
272 if (!mutex) {
273 // Create a new mutex for the specified address if one does not already exist
274 mutex = Mutex::Create(holding_thread, mutex_addr);
275 mutex->name = Common::StringFromFormat("mutex-%llx", mutex_addr);
276 }
277
278 ASSERT(holding_thread == mutex->GetHoldingThread());
279
280 return WaitSynchronization1(mutex, requesting_thread.get());
281} 265}
282 266
283/// Unlock a mutex 267/// Unlock a mutex
284static ResultCode ArbitrateUnlock(VAddr mutex_addr) { 268static ResultCode ArbitrateUnlock(VAddr mutex_addr) {
285 LOG_TRACE(Kernel_SVC, "called mutex_addr=0x%llx", mutex_addr); 269 LOG_TRACE(Kernel_SVC, "called mutex_addr=0x%llx", mutex_addr);
286 270
287 SharedPtr<Mutex> mutex = g_object_address_table.Get<Mutex>(mutex_addr); 271 return Mutex::Release(mutex_addr);
288 ASSERT(mutex);
289
290 return mutex->Release(GetCurrentThread());
291} 272}
292 273
293/// Break program execution 274/// Break program execution
@@ -412,11 +393,6 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
412 } 393 }
413 394
414 thread->SetPriority(priority); 395 thread->SetPriority(priority);
415 thread->UpdatePriority();
416
417 // Update the mutexes that this thread is waiting for
418 for (auto& mutex : thread->pending_mutexes)
419 mutex->UpdatePriority();
420 396
421 Core::System::GetInstance().PrepareReschedule(); 397 Core::System::GetInstance().PrepareReschedule();
422 return RESULT_SUCCESS; 398 return RESULT_SUCCESS;
@@ -634,103 +610,75 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var
634 SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle); 610 SharedPtr<Thread> thread = g_handle_table.Get<Thread>(thread_handle);
635 ASSERT(thread); 611 ASSERT(thread);
636 612
637 SharedPtr<Mutex> mutex = g_object_address_table.Get<Mutex>(mutex_addr); 613 CASCADE_CODE(Mutex::Release(mutex_addr));
638 if (!mutex) {
639 // Create a new mutex for the specified address if one does not already exist
640 mutex = Mutex::Create(thread, mutex_addr);
641 mutex->name = Common::StringFromFormat("mutex-%llx", mutex_addr);
642 }
643
644 SharedPtr<ConditionVariable> condition_variable =
645 g_object_address_table.Get<ConditionVariable>(condition_variable_addr);
646 if (!condition_variable) {
647 // Create a new condition_variable for the specified address if one does not already exist
648 condition_variable = ConditionVariable::Create(condition_variable_addr).Unwrap();
649 condition_variable->name =
650 Common::StringFromFormat("condition-variable-%llx", condition_variable_addr);
651 }
652
653 if (condition_variable->mutex_addr) {
654 // Previously created the ConditionVariable using WaitProcessWideKeyAtomic, verify
655 // everything is correct
656 ASSERT(condition_variable->mutex_addr == mutex_addr);
657 } else {
658 // Previously created the ConditionVariable using SignalProcessWideKey, set the mutex
659 // associated with it
660 condition_variable->mutex_addr = mutex_addr;
661 }
662 614
663 if (mutex->GetOwnerHandle()) { 615 SharedPtr<Thread> current_thread = GetCurrentThread();
664 // Release the mutex if the current thread is holding it 616 current_thread->condvar_wait_address = condition_variable_addr;
665 mutex->Release(thread.get()); 617 current_thread->mutex_wait_address = mutex_addr;
666 } 618 current_thread->wait_handle = thread_handle;
619 current_thread->status = THREADSTATUS_WAIT_MUTEX;
620 current_thread->wakeup_callback = nullptr;
667 621
668 auto wakeup_callback = [mutex, nano_seconds](ThreadWakeupReason reason, 622 current_thread->WakeAfterDelay(nano_seconds);
669 SharedPtr<Thread> thread,
670 SharedPtr<WaitObject> object, size_t index) {
671 ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY);
672 623
673 if (reason == ThreadWakeupReason::Timeout) { 624 // Note: Deliberately don't attempt to inherit the lock owner's priority.
674 thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
675 return true;
676 }
677 625
678 ASSERT(reason == ThreadWakeupReason::Signal); 626 Core::System::GetInstance().PrepareReschedule();
627 return RESULT_SUCCESS;
628}
679 629
680 // Now try to acquire the mutex and don't resume if it's not available. 630/// Signal process wide key
681 if (!mutex->ShouldWait(thread.get())) { 631static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target) {
682 mutex->Acquire(thread.get()); 632 LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x%llx, target=0x%08x",
683 thread->SetWaitSynchronizationResult(RESULT_SUCCESS); 633 condition_variable_addr, target);
684 return true;
685 }
686 634
687 if (nano_seconds == 0) { 635 u32 processed = 0;
688 thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); 636 auto& thread_list = Core::System::GetInstance().Scheduler().GetThreadList();
689 return true;
690 }
691 637
692 thread->wait_objects = {mutex}; 638 for (auto& thread : thread_list) {
693 mutex->AddWaitingThread(thread); 639 if (thread->condvar_wait_address != condition_variable_addr)
694 thread->status = THREADSTATUS_WAIT_SYNCH_ANY; 640 continue;
695 641
696 // Create an event to wake the thread up after the 642 // Only process up to 'target' threads, unless 'target' is -1, in which case process
697 // specified nanosecond delay has passed 643 // them all.
698 thread->WakeAfterDelay(nano_seconds); 644 if (target != -1 && processed >= target)
699 thread->wakeup_callback = DefaultThreadWakeupCallback; 645 break;
700 646
701 Core::System::GetInstance().PrepareReschedule(); 647 // If the mutex is not yet acquired, acquire it.
648 u32 mutex_val = Memory::Read32(thread->mutex_wait_address);
702 649
703 return false; 650 if (mutex_val == 0) {
704 }; 651 // We were able to acquire the mutex, resume this thread.
705 CASCADE_CODE( 652 Memory::Write32(thread->mutex_wait_address, thread->wait_handle);
706 WaitSynchronization1(condition_variable, thread.get(), nano_seconds, wakeup_callback)); 653 ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX);
654 thread->ResumeFromWait();
707 655
708 return RESULT_SUCCESS; 656 auto lock_owner = thread->lock_owner;
709} 657 if (lock_owner)
658 lock_owner->RemoveMutexWaiter(thread);
710 659
711/// Signal process wide key 660 thread->lock_owner = nullptr;
712static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target) { 661 thread->mutex_wait_address = 0;
713 LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x%llx, target=0x%08x", 662 thread->condvar_wait_address = 0;
714 condition_variable_addr, target); 663 thread->wait_handle = 0;
664 } else {
665 // Couldn't acquire the mutex, block the thread.
666 Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
667 auto owner = g_handle_table.Get<Thread>(owner_handle);
668 ASSERT(owner);
669 ASSERT(thread->status != THREADSTATUS_RUNNING);
670 thread->status = THREADSTATUS_WAIT_MUTEX;
671 thread->wakeup_callback = nullptr;
715 672
716 // Wakeup all or one thread - Any other value is unimplemented 673 // Signal that the mutex now has a waiting thread.
717 ASSERT(target == -1 || target == 1); 674 Memory::Write32(thread->mutex_wait_address, mutex_val | Mutex::MutexHasWaitersFlag);
718 675
719 SharedPtr<ConditionVariable> condition_variable = 676 owner->AddMutexWaiter(thread);
720 g_object_address_table.Get<ConditionVariable>(condition_variable_addr);
721 if (!condition_variable) {
722 // Create a new condition_variable for the specified address if one does not already exist
723 condition_variable = ConditionVariable::Create(condition_variable_addr).Unwrap();
724 condition_variable->name =
725 Common::StringFromFormat("condition-variable-%llx", condition_variable_addr);
726 }
727 677
728 CASCADE_CODE(condition_variable->Release(target)); 678 Core::System::GetInstance().PrepareReschedule();
679 }
729 680
730 if (condition_variable->mutex_addr) { 681 ++processed;
731 // If a mutex was created for this condition_variable, wait the current thread on it
732 SharedPtr<Mutex> mutex = g_object_address_table.Get<Mutex>(condition_variable->mutex_addr);
733 return WaitSynchronization1(mutex, GetCurrentThread());
734 } 682 }
735 683
736 return RESULT_SUCCESS; 684 return RESULT_SUCCESS;
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index f3a8aa4aa..36222d45f 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -77,9 +77,6 @@ void Thread::Stop() {
77 } 77 }
78 wait_objects.clear(); 78 wait_objects.clear();
79 79
80 // Release all the mutexes that this thread holds
81 ReleaseThreadMutexes(this);
82
83 // Mark the TLS slot in the thread's page as free. 80 // Mark the TLS slot in the thread's page as free.
84 u64 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE; 81 u64 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE;
85 u64 tls_slot = 82 u64 tls_slot =
@@ -126,6 +123,19 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
126 resume = thread->wakeup_callback(ThreadWakeupReason::Timeout, thread, nullptr, 0); 123 resume = thread->wakeup_callback(ThreadWakeupReason::Timeout, thread, nullptr, 0);
127 } 124 }
128 125
126 if (thread->mutex_wait_address != 0 || thread->condvar_wait_address != 0 ||
127 thread->wait_handle) {
128 ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX);
129 thread->mutex_wait_address = 0;
130 thread->condvar_wait_address = 0;
131 thread->wait_handle = 0;
132
133 auto lock_owner = thread->lock_owner;
134 // Threads waking up by timeout from WaitProcessWideKey do not perform priority inheritance
135 // and don't have a lock owner.
136 ASSERT(lock_owner == nullptr);
137 }
138
129 if (resume) 139 if (resume)
130 thread->ResumeFromWait(); 140 thread->ResumeFromWait();
131} 141}
@@ -151,6 +161,7 @@ void Thread::ResumeFromWait() {
151 case THREADSTATUS_WAIT_HLE_EVENT: 161 case THREADSTATUS_WAIT_HLE_EVENT:
152 case THREADSTATUS_WAIT_SLEEP: 162 case THREADSTATUS_WAIT_SLEEP:
153 case THREADSTATUS_WAIT_IPC: 163 case THREADSTATUS_WAIT_IPC:
164 case THREADSTATUS_WAIT_MUTEX:
154 break; 165 break;
155 166
156 case THREADSTATUS_READY: 167 case THREADSTATUS_READY:
@@ -256,7 +267,9 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
256 thread->last_running_ticks = CoreTiming::GetTicks(); 267 thread->last_running_ticks = CoreTiming::GetTicks();
257 thread->processor_id = processor_id; 268 thread->processor_id = processor_id;
258 thread->wait_objects.clear(); 269 thread->wait_objects.clear();
259 thread->wait_address = 0; 270 thread->mutex_wait_address = 0;
271 thread->condvar_wait_address = 0;
272 thread->wait_handle = 0;
260 thread->name = std::move(name); 273 thread->name = std::move(name);
261 thread->callback_handle = wakeup_callback_handle_table.Create(thread).Unwrap(); 274 thread->callback_handle = wakeup_callback_handle_table.Create(thread).Unwrap();
262 thread->owner_process = owner_process; 275 thread->owner_process = owner_process;
@@ -317,17 +330,8 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
317void Thread::SetPriority(u32 priority) { 330void Thread::SetPriority(u32 priority) {
318 ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST, 331 ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST,
319 "Invalid priority value."); 332 "Invalid priority value.");
320 Core::System::GetInstance().Scheduler().SetThreadPriority(this, priority); 333 nominal_priority = priority;
321 nominal_priority = current_priority = priority; 334 UpdatePriority();
322}
323
324void Thread::UpdatePriority() {
325 u32 best_priority = nominal_priority;
326 for (auto& mutex : held_mutexes) {
327 if (mutex->priority < best_priority)
328 best_priority = mutex->priority;
329 }
330 BoostPriority(best_priority);
331} 335}
332 336
333void Thread::BoostPriority(u32 priority) { 337void Thread::BoostPriority(u32 priority) {
@@ -377,6 +381,38 @@ VAddr Thread::GetCommandBufferAddress() const {
377 return GetTLSAddress() + CommandHeaderOffset; 381 return GetTLSAddress() + CommandHeaderOffset;
378} 382}
379 383
384void Thread::AddMutexWaiter(SharedPtr<Thread> thread) {
385 thread->lock_owner = this;
386 wait_mutex_threads.emplace_back(std::move(thread));
387 UpdatePriority();
388}
389
390void Thread::RemoveMutexWaiter(SharedPtr<Thread> thread) {
391 boost::remove_erase(wait_mutex_threads, thread);
392 thread->lock_owner = nullptr;
393 UpdatePriority();
394}
395
396void Thread::UpdatePriority() {
397 // Find the highest priority among all the threads that are waiting for this thread's lock
398 u32 new_priority = nominal_priority;
399 for (const auto& thread : wait_mutex_threads) {
400 if (thread->nominal_priority < new_priority)
401 new_priority = thread->nominal_priority;
402 }
403
404 if (new_priority == current_priority)
405 return;
406
407 Core::System::GetInstance().Scheduler().SetThreadPriority(this, new_priority);
408
409 current_priority = new_priority;
410
411 // Recursively update the priority of the thread that depends on the priority of this one.
412 if (lock_owner)
413 lock_owner->UpdatePriority();
414}
415
380//////////////////////////////////////////////////////////////////////////////////////////////////// 416////////////////////////////////////////////////////////////////////////////////////////////////////
381 417
382/** 418/**
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index dbf47e269..e0a3c0934 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -18,7 +18,7 @@
18enum ThreadPriority : u32 { 18enum ThreadPriority : u32 {
19 THREADPRIO_HIGHEST = 0, ///< Highest thread priority 19 THREADPRIO_HIGHEST = 0, ///< Highest thread priority
20 THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps 20 THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps
21 THREADPRIO_DEFAULT = 48, ///< Default thread priority for userland apps 21 THREADPRIO_DEFAULT = 44, ///< Default thread priority for userland apps
22 THREADPRIO_LOWEST = 63, ///< Lowest thread priority 22 THREADPRIO_LOWEST = 63, ///< Lowest thread priority
23}; 23};
24 24
@@ -43,6 +43,7 @@ enum ThreadStatus {
43 THREADSTATUS_WAIT_IPC, ///< Waiting for the reply from an IPC request 43 THREADSTATUS_WAIT_IPC, ///< Waiting for the reply from an IPC request
44 THREADSTATUS_WAIT_SYNCH_ANY, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false 44 THREADSTATUS_WAIT_SYNCH_ANY, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false
45 THREADSTATUS_WAIT_SYNCH_ALL, ///< Waiting due to WaitSynchronizationN with wait_all = true 45 THREADSTATUS_WAIT_SYNCH_ALL, ///< Waiting due to WaitSynchronizationN with wait_all = true
46 THREADSTATUS_WAIT_MUTEX, ///< Waiting due to an ArbitrateLock/WaitProcessWideKey svc
46 THREADSTATUS_DORMANT, ///< Created but not yet made ready 47 THREADSTATUS_DORMANT, ///< Created but not yet made ready
47 THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated 48 THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated
48}; 49};
@@ -54,7 +55,6 @@ enum class ThreadWakeupReason {
54 55
55namespace Kernel { 56namespace Kernel {
56 57
57class Mutex;
58class Process; 58class Process;
59 59
60class Thread final : public WaitObject { 60class Thread final : public WaitObject {
@@ -104,17 +104,20 @@ public:
104 void SetPriority(u32 priority); 104 void SetPriority(u32 priority);
105 105
106 /** 106 /**
107 * Boost's a thread's priority to the best priority among the thread's held mutexes.
108 * This prevents priority inversion via priority inheritance.
109 */
110 void UpdatePriority();
111
112 /**
113 * Temporarily boosts the thread's priority until the next time it is scheduled 107 * Temporarily boosts the thread's priority until the next time it is scheduled
114 * @param priority The new priority 108 * @param priority The new priority
115 */ 109 */
116 void BoostPriority(u32 priority); 110 void BoostPriority(u32 priority);
117 111
112 /// Adds a thread to the list of threads that are waiting for a lock held by this thread.
113 void AddMutexWaiter(SharedPtr<Thread> thread);
114
115 /// Removes a thread from the list of threads that are waiting for a lock held by this thread.
116 void RemoveMutexWaiter(SharedPtr<Thread> thread);
117
118 /// Recalculates the current priority taking into account priority inheritance.
119 void UpdatePriority();
120
118 /** 121 /**
119 * Gets the thread's thread ID 122 * Gets the thread's thread ID
120 * @return The thread's ID 123 * @return The thread's ID
@@ -205,19 +208,22 @@ public:
205 208
206 VAddr tls_address; ///< Virtual address of the Thread Local Storage of the thread 209 VAddr tls_address; ///< Virtual address of the Thread Local Storage of the thread
207 210
208 /// Mutexes currently held by this thread, which will be released when it exits.
209 boost::container::flat_set<SharedPtr<Mutex>> held_mutexes;
210
211 /// Mutexes that this thread is currently waiting for.
212 boost::container::flat_set<SharedPtr<Mutex>> pending_mutexes;
213
214 SharedPtr<Process> owner_process; ///< Process that owns this thread 211 SharedPtr<Process> owner_process; ///< Process that owns this thread
215 212
216 /// Objects that the thread is waiting on, in the same order as they were 213 /// Objects that the thread is waiting on, in the same order as they were
217 // passed to WaitSynchronization1/N. 214 // passed to WaitSynchronization1/N.
218 std::vector<SharedPtr<WaitObject>> wait_objects; 215 std::vector<SharedPtr<WaitObject>> wait_objects;
219 216
220 VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address 217 /// List of threads that are waiting for a mutex that is held by this thread.
218 std::vector<SharedPtr<Thread>> wait_mutex_threads;
219
220 /// Thread that owns the lock that this thread is waiting for.
221 SharedPtr<Thread> lock_owner;
222
223 // If waiting on a ConditionVariable, this is the ConditionVariable address
224 VAddr condvar_wait_address;
225 VAddr mutex_wait_address; ///< If waiting on a Mutex, this is the mutex address
226 Handle wait_handle; ///< The handle used to wait for the mutex.
221 227
222 std::string name; 228 std::string name;
223 229
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index 03a4fed59..e4ff2e267 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -9,7 +9,8 @@
9#include "core/core_timing.h" 9#include "core/core_timing.h"
10#include "core/hle/service/nvflinger/buffer_queue.h" 10#include "core/hle/service/nvflinger/buffer_queue.h"
11 11
12namespace Service::NVFlinger { 12namespace Service {
13namespace NVFlinger {
13 14
14BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) { 15BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) {
15 native_handle = Kernel::Event::Create(Kernel::ResetType::OneShot, "BufferQueue NativeHandle"); 16 native_handle = Kernel::Event::Create(Kernel::ResetType::OneShot, "BufferQueue NativeHandle");
@@ -110,4 +111,5 @@ void BufferQueue::SetBufferWaitEvent(Kernel::SharedPtr<Kernel::Event>&& wait_eve
110 buffer_wait_event = std::move(wait_event); 111 buffer_wait_event = std::move(wait_event);
111} 112}
112 113
113} // namespace Service::NVFlinger 114} // namespace NVFlinger
115} // namespace Service
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index 95adc4706..1de5767cb 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -13,7 +13,8 @@ namespace CoreTiming {
13struct EventType; 13struct EventType;
14} 14}
15 15
16namespace Service::NVFlinger { 16namespace Service {
17namespace NVFlinger {
17 18
18struct IGBPBuffer { 19struct IGBPBuffer {
19 u32_le magic; 20 u32_le magic;
@@ -97,4 +98,5 @@ private:
97 Kernel::SharedPtr<Kernel::Event> buffer_wait_event; 98 Kernel::SharedPtr<Kernel::Event> buffer_wait_event;
98}; 99};
99 100
100} // namespace Service::NVFlinger 101} // namespace NVFlinger
102} // namespace Service
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index b5133e4d6..3853cfa1a 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -137,7 +137,7 @@ ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
137 process->address_mappings = default_address_mappings; 137 process->address_mappings = default_address_mappings;
138 process->resource_limit = 138 process->resource_limit =
139 Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); 139 Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
140 process->Run(base_addr, 48, Memory::DEFAULT_STACK_SIZE); 140 process->Run(base_addr, THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
141 141
142 is_loaded = true; 142 is_loaded = true;
143 return ResultStatus::Success; 143 return ResultStatus::Success;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 3bc10ed0d..962bed2ab 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -165,7 +165,7 @@ ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
165 process->address_mappings = default_address_mappings; 165 process->address_mappings = default_address_mappings;
166 process->resource_limit = 166 process->resource_limit =
167 Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); 167 Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
168 process->Run(Memory::PROCESS_IMAGE_VADDR, 48, Memory::DEFAULT_STACK_SIZE); 168 process->Run(Memory::PROCESS_IMAGE_VADDR, THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
169 169
170 is_loaded = true; 170 is_loaded = true;
171 return ResultStatus::Success; 171 return ResultStatus::Success;
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index cae2864e5..acc4c2e0b 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -6,8 +6,8 @@
6#include "yuzu/util/util.h" 6#include "yuzu/util/util.h"
7 7
8#include "core/core.h" 8#include "core/core.h"
9#include "core/hle/kernel/condition_variable.h"
10#include "core/hle/kernel/event.h" 9#include "core/hle/kernel/event.h"
10#include "core/hle/kernel/handle_table.h"
11#include "core/hle/kernel/mutex.h" 11#include "core/hle/kernel/mutex.h"
12#include "core/hle/kernel/thread.h" 12#include "core/hle/kernel/thread.h"
13#include "core/hle/kernel/timer.h" 13#include "core/hle/kernel/timer.h"
@@ -67,6 +67,29 @@ QString WaitTreeText::GetText() const {
67 return text; 67 return text;
68} 68}
69 69
70WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address) : mutex_address(mutex_address) {
71 mutex_value = Memory::Read32(mutex_address);
72 owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Mutex::MutexOwnerMask);
73 owner = Kernel::g_handle_table.Get<Kernel::Thread>(owner_handle);
74}
75
76QString WaitTreeMutexInfo::GetText() const {
77 return tr("waiting for mutex 0x%1").arg(mutex_address, 16, 16, QLatin1Char('0'));
78}
79
80std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeMutexInfo::GetChildren() const {
81 std::vector<std::unique_ptr<WaitTreeItem>> list;
82
83 bool has_waiters = (mutex_value & Kernel::Mutex::MutexHasWaitersFlag) != 0;
84
85 list.push_back(std::make_unique<WaitTreeText>(tr("has waiters: %1").arg(has_waiters)));
86 list.push_back(std::make_unique<WaitTreeText>(
87 tr("owner handle: 0x%1").arg(owner_handle, 8, 16, QLatin1Char('0'))));
88 if (owner != nullptr)
89 list.push_back(std::make_unique<WaitTreeThread>(*owner));
90 return list;
91}
92
70WaitTreeWaitObject::WaitTreeWaitObject(const Kernel::WaitObject& o) : object(o) {} 93WaitTreeWaitObject::WaitTreeWaitObject(const Kernel::WaitObject& o) : object(o) {}
71 94
72bool WaitTreeExpandableItem::IsExpandable() const { 95bool WaitTreeExpandableItem::IsExpandable() const {
@@ -84,11 +107,6 @@ std::unique_ptr<WaitTreeWaitObject> WaitTreeWaitObject::make(const Kernel::WaitO
84 switch (object.GetHandleType()) { 107 switch (object.GetHandleType()) {
85 case Kernel::HandleType::Event: 108 case Kernel::HandleType::Event:
86 return std::make_unique<WaitTreeEvent>(static_cast<const Kernel::Event&>(object)); 109 return std::make_unique<WaitTreeEvent>(static_cast<const Kernel::Event&>(object));
87 case Kernel::HandleType::Mutex:
88 return std::make_unique<WaitTreeMutex>(static_cast<const Kernel::Mutex&>(object));
89 case Kernel::HandleType::ConditionVariable:
90 return std::make_unique<WaitTreeConditionVariable>(
91 static_cast<const Kernel::ConditionVariable&>(object));
92 case Kernel::HandleType::Timer: 110 case Kernel::HandleType::Timer:
93 return std::make_unique<WaitTreeTimer>(static_cast<const Kernel::Timer&>(object)); 111 return std::make_unique<WaitTreeTimer>(static_cast<const Kernel::Timer&>(object));
94 case Kernel::HandleType::Thread: 112 case Kernel::HandleType::Thread:
@@ -160,6 +178,9 @@ QString WaitTreeThread::GetText() const {
160 case THREADSTATUS_WAIT_SYNCH_ANY: 178 case THREADSTATUS_WAIT_SYNCH_ANY:
161 status = tr("waiting for objects"); 179 status = tr("waiting for objects");
162 break; 180 break;
181 case THREADSTATUS_WAIT_MUTEX:
182 status = tr("waiting for mutex");
183 break;
163 case THREADSTATUS_DORMANT: 184 case THREADSTATUS_DORMANT:
164 status = tr("dormant"); 185 status = tr("dormant");
165 break; 186 break;
@@ -186,6 +207,7 @@ QColor WaitTreeThread::GetColor() const {
186 return QColor(Qt::GlobalColor::darkYellow); 207 return QColor(Qt::GlobalColor::darkYellow);
187 case THREADSTATUS_WAIT_SYNCH_ALL: 208 case THREADSTATUS_WAIT_SYNCH_ALL:
188 case THREADSTATUS_WAIT_SYNCH_ANY: 209 case THREADSTATUS_WAIT_SYNCH_ANY:
210 case THREADSTATUS_WAIT_MUTEX:
189 return QColor(Qt::GlobalColor::red); 211 return QColor(Qt::GlobalColor::red);
190 case THREADSTATUS_DORMANT: 212 case THREADSTATUS_DORMANT:
191 return QColor(Qt::GlobalColor::darkCyan); 213 return QColor(Qt::GlobalColor::darkCyan);
@@ -225,11 +247,11 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
225 list.push_back(std::make_unique<WaitTreeText>( 247 list.push_back(std::make_unique<WaitTreeText>(
226 tr("last running ticks = %1").arg(thread.last_running_ticks))); 248 tr("last running ticks = %1").arg(thread.last_running_ticks)));
227 249
228 if (thread.held_mutexes.empty()) { 250 if (thread.mutex_wait_address != 0)
229 list.push_back(std::make_unique<WaitTreeText>(tr("not holding mutex"))); 251 list.push_back(std::make_unique<WaitTreeMutexInfo>(thread.mutex_wait_address));
230 } else { 252 else
231 list.push_back(std::make_unique<WaitTreeMutexList>(thread.held_mutexes)); 253 list.push_back(std::make_unique<WaitTreeText>(tr("not waiting for mutex")));
232 } 254
233 if (thread.status == THREADSTATUS_WAIT_SYNCH_ANY || 255 if (thread.status == THREADSTATUS_WAIT_SYNCH_ANY ||
234 thread.status == THREADSTATUS_WAIT_SYNCH_ALL) { 256 thread.status == THREADSTATUS_WAIT_SYNCH_ALL) {
235 list.push_back(std::make_unique<WaitTreeObjectList>(thread.wait_objects, 257 list.push_back(std::make_unique<WaitTreeObjectList>(thread.wait_objects,
@@ -250,33 +272,6 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeEvent::GetChildren() const {
250 return list; 272 return list;
251} 273}
252 274
253WaitTreeMutex::WaitTreeMutex(const Kernel::Mutex& object) : WaitTreeWaitObject(object) {}
254
255std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeMutex::GetChildren() const {
256 std::vector<std::unique_ptr<WaitTreeItem>> list(WaitTreeWaitObject::GetChildren());
257
258 const auto& mutex = static_cast<const Kernel::Mutex&>(object);
259 if (mutex.GetHasWaiters()) {
260 list.push_back(std::make_unique<WaitTreeText>(tr("locked by thread:")));
261 list.push_back(std::make_unique<WaitTreeThread>(*mutex.GetHoldingThread()));
262 } else {
263 list.push_back(std::make_unique<WaitTreeText>(tr("free")));
264 }
265 return list;
266}
267
268WaitTreeConditionVariable::WaitTreeConditionVariable(const Kernel::ConditionVariable& object)
269 : WaitTreeWaitObject(object) {}
270
271std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeConditionVariable::GetChildren() const {
272 std::vector<std::unique_ptr<WaitTreeItem>> list(WaitTreeWaitObject::GetChildren());
273
274 const auto& condition_variable = static_cast<const Kernel::ConditionVariable&>(object);
275 list.push_back(std::make_unique<WaitTreeText>(
276 tr("available count = %1").arg(condition_variable.GetAvailableCount())));
277 return list;
278}
279
280WaitTreeTimer::WaitTreeTimer(const Kernel::Timer& object) : WaitTreeWaitObject(object) {} 275WaitTreeTimer::WaitTreeTimer(const Kernel::Timer& object) : WaitTreeWaitObject(object) {}
281 276
282std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeTimer::GetChildren() const { 277std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeTimer::GetChildren() const {
@@ -293,21 +288,6 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeTimer::GetChildren() const {
293 return list; 288 return list;
294} 289}
295 290
296WaitTreeMutexList::WaitTreeMutexList(
297 const boost::container::flat_set<Kernel::SharedPtr<Kernel::Mutex>>& list)
298 : mutex_list(list) {}
299
300QString WaitTreeMutexList::GetText() const {
301 return tr("holding mutexes");
302}
303
304std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeMutexList::GetChildren() const {
305 std::vector<std::unique_ptr<WaitTreeItem>> list(mutex_list.size());
306 std::transform(mutex_list.begin(), mutex_list.end(), list.begin(),
307 [](const auto& t) { return std::make_unique<WaitTreeMutex>(*t); });
308 return list;
309}
310
311WaitTreeThreadList::WaitTreeThreadList(const std::vector<Kernel::SharedPtr<Kernel::Thread>>& list) 291WaitTreeThreadList::WaitTreeThreadList(const std::vector<Kernel::SharedPtr<Kernel::Thread>>& list)
312 : thread_list(list) {} 292 : thread_list(list) {}
313 293
diff --git a/src/yuzu/debugger/wait_tree.h b/src/yuzu/debugger/wait_tree.h
index e538174eb..300ba9ae4 100644
--- a/src/yuzu/debugger/wait_tree.h
+++ b/src/yuzu/debugger/wait_tree.h
@@ -16,8 +16,6 @@ class EmuThread;
16namespace Kernel { 16namespace Kernel {
17class WaitObject; 17class WaitObject;
18class Event; 18class Event;
19class Mutex;
20class ConditionVariable;
21class Thread; 19class Thread;
22class Timer; 20class Timer;
23} // namespace Kernel 21} // namespace Kernel
@@ -61,6 +59,20 @@ public:
61 bool IsExpandable() const override; 59 bool IsExpandable() const override;
62}; 60};
63 61
62class WaitTreeMutexInfo : public WaitTreeExpandableItem {
63 Q_OBJECT
64public:
65 explicit WaitTreeMutexInfo(VAddr mutex_address);
66 QString GetText() const override;
67 std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
68
69private:
70 VAddr mutex_address;
71 u32 mutex_value;
72 Kernel::Handle owner_handle;
73 Kernel::SharedPtr<Kernel::Thread> owner;
74};
75
64class WaitTreeWaitObject : public WaitTreeExpandableItem { 76class WaitTreeWaitObject : public WaitTreeExpandableItem {
65 Q_OBJECT 77 Q_OBJECT
66public: 78public:
@@ -104,20 +116,6 @@ public:
104 std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; 116 std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
105}; 117};
106 118
107class WaitTreeMutex : public WaitTreeWaitObject {
108 Q_OBJECT
109public:
110 explicit WaitTreeMutex(const Kernel::Mutex& object);
111 std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
112};
113
114class WaitTreeConditionVariable : public WaitTreeWaitObject {
115 Q_OBJECT
116public:
117 explicit WaitTreeConditionVariable(const Kernel::ConditionVariable& object);
118 std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
119};
120
121class WaitTreeTimer : public WaitTreeWaitObject { 119class WaitTreeTimer : public WaitTreeWaitObject {
122 Q_OBJECT 120 Q_OBJECT
123public: 121public:
@@ -125,19 +123,6 @@ public:
125 std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; 123 std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
126}; 124};
127 125
128class WaitTreeMutexList : public WaitTreeExpandableItem {
129 Q_OBJECT
130public:
131 explicit WaitTreeMutexList(
132 const boost::container::flat_set<Kernel::SharedPtr<Kernel::Mutex>>& list);
133
134 QString GetText() const override;
135 std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
136
137private:
138 const boost::container::flat_set<Kernel::SharedPtr<Kernel::Mutex>>& mutex_list;
139};
140
141class WaitTreeThreadList : public WaitTreeExpandableItem { 126class WaitTreeThreadList : public WaitTreeExpandableItem {
142 Q_OBJECT 127 Q_OBJECT
143public: 128public: