diff options
| author | 2018-04-23 16:33:00 -0400 | |
|---|---|---|
| committer | 2018-04-23 16:33:00 -0400 | |
| commit | 0214351f4f0e9377792f8ceb657e3a47aba334d1 (patch) | |
| tree | f772d4dbaf3d804497740c6c67d1110f20fd010f /src/core | |
| parent | Merge pull request #384 from Subv/nvhost-remap (diff) | |
| parent | Kernel: Implemented mutex priority inheritance. (diff) | |
| download | yuzu-0214351f4f0e9377792f8ceb657e3a47aba334d1.tar.gz yuzu-0214351f4f0e9377792f8ceb657e3a47aba334d1.tar.xz yuzu-0214351f4f0e9377792f8ceb657e3a47aba334d1.zip | |
Merge pull request #370 from Subv/sync_primitives
Kernel: Reworked the new kernel synchronization primitives.
Diffstat (limited to 'src/core')
| -rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/core/hle/kernel/condition_variable.cpp | 64 | ||||
| -rw-r--r-- | src/core/hle/kernel/condition_variable.h | 63 | ||||
| -rw-r--r-- | src/core/hle/kernel/errors.h | 1 | ||||
| -rw-r--r-- | src/core/hle/kernel/kernel.h | 4 | ||||
| -rw-r--r-- | src/core/hle/kernel/mutex.cpp | 179 | ||||
| -rw-r--r-- | src/core/hle/kernel/mutex.h | 88 | ||||
| -rw-r--r-- | src/core/hle/kernel/svc.cpp | 162 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.cpp | 66 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.h | 36 | ||||
| -rw-r--r-- | src/core/hle/service/nvflinger/buffer_queue.cpp | 6 | ||||
| -rw-r--r-- | src/core/hle/service/nvflinger/buffer_queue.h | 6 | ||||
| -rw-r--r-- | src/core/loader/nro.cpp | 2 | ||||
| -rw-r--r-- | src/core/loader/nso.cpp | 2 |
14 files changed, 238 insertions, 443 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 | |||
| 12 | namespace Kernel { | ||
| 13 | |||
| 14 | ConditionVariable::ConditionVariable() {} | ||
| 15 | ConditionVariable::~ConditionVariable() {} | ||
| 16 | |||
| 17 | ResultVal<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 | |||
| 31 | bool ConditionVariable::ShouldWait(Thread* thread) const { | ||
| 32 | return GetAvailableCount() <= 0; | ||
| 33 | } | ||
| 34 | |||
| 35 | void ConditionVariable::Acquire(Thread* thread) { | ||
| 36 | if (GetAvailableCount() <= 0) | ||
| 37 | return; | ||
| 38 | |||
| 39 | SetAvailableCount(GetAvailableCount() - 1); | ||
| 40 | } | ||
| 41 | |||
| 42 | ResultCode 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 | |||
| 56 | s32 ConditionVariable::GetAvailableCount() const { | ||
| 57 | return Memory::Read32(guest_addr); | ||
| 58 | } | ||
| 59 | |||
| 60 | void 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 | |||
| 14 | namespace Kernel { | ||
| 15 | |||
| 16 | class ConditionVariable final : public WaitObject { | ||
| 17 | public: | ||
| 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 | |||
| 58 | private: | ||
| 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; | |||
| 18 | enum class HandleType : u32 { | 18 | enum 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 | ||
| 16 | namespace Kernel { | 17 | namespace Kernel { |
| 17 | 18 | ||
| 18 | void 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); | 21 | static 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 | ||
| 27 | Mutex::Mutex() {} | 24 | SharedPtr<Thread> highest_priority_thread; |
| 28 | Mutex::~Mutex() {} | 25 | u32 num_waiters = 0; |
| 29 | 26 | ||
| 30 | SharedPtr<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 | ||
| 48 | bool 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(); | 44 | static 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 | ||
| 53 | void Mutex::Acquire(Thread* thread) { | 58 | ResultCode 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 | ||
| 63 | ResultCode 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 | ||
| 80 | void Mutex::AddWaitingThread(SharedPtr<Thread> thread) { | 97 | ResultCode 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 | |||
| 87 | void 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 | ||
| 95 | void 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 | ||
| 111 | Handle 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 | ||
| 116 | SharedPtr<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 | ||
| 121 | void 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 | ||
| 127 | bool 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 | ||
| 132 | void 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 | ||
| 16 | class Thread; | 16 | class Thread; |
| 17 | 17 | ||
| 18 | class Mutex final : public WaitObject { | 18 | class Mutex final { |
| 19 | public: | 19 | public: |
| 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 | ||
| 80 | private: | 32 | private: |
| 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 | */ | ||
| 99 | void 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 |
| 284 | static ResultCode ArbitrateUnlock(VAddr mutex_addr) { | 268 | static 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())) { | 631 | static 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; |
| 712 | static 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, | |||
| 317 | void Thread::SetPriority(u32 priority) { | 330 | void 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 | |||
| 324 | void 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 | ||
| 333 | void Thread::BoostPriority(u32 priority) { | 337 | void Thread::BoostPriority(u32 priority) { |
| @@ -377,6 +381,38 @@ VAddr Thread::GetCommandBufferAddress() const { | |||
| 377 | return GetTLSAddress() + CommandHeaderOffset; | 381 | return GetTLSAddress() + CommandHeaderOffset; |
| 378 | } | 382 | } |
| 379 | 383 | ||
| 384 | void Thread::AddMutexWaiter(SharedPtr<Thread> thread) { | ||
| 385 | thread->lock_owner = this; | ||
| 386 | wait_mutex_threads.emplace_back(std::move(thread)); | ||
| 387 | UpdatePriority(); | ||
| 388 | } | ||
| 389 | |||
| 390 | void Thread::RemoveMutexWaiter(SharedPtr<Thread> thread) { | ||
| 391 | boost::remove_erase(wait_mutex_threads, thread); | ||
| 392 | thread->lock_owner = nullptr; | ||
| 393 | UpdatePriority(); | ||
| 394 | } | ||
| 395 | |||
| 396 | void 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 @@ | |||
| 18 | enum ThreadPriority : u32 { | 18 | enum 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 | ||
| 55 | namespace Kernel { | 56 | namespace Kernel { |
| 56 | 57 | ||
| 57 | class Mutex; | ||
| 58 | class Process; | 58 | class Process; |
| 59 | 59 | ||
| 60 | class Thread final : public WaitObject { | 60 | class 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 | ||
| 12 | namespace Service::NVFlinger { | 12 | namespace Service { |
| 13 | namespace NVFlinger { | ||
| 13 | 14 | ||
| 14 | BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) { | 15 | BufferQueue::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 { | |||
| 13 | struct EventType; | 13 | struct EventType; |
| 14 | } | 14 | } |
| 15 | 15 | ||
| 16 | namespace Service::NVFlinger { | 16 | namespace Service { |
| 17 | namespace NVFlinger { | ||
| 17 | 18 | ||
| 18 | struct IGBPBuffer { | 19 | struct 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; |