From e4e12386d2d3511661e00e0b588132fe4180584f Mon Sep 17 00:00:00 2001 From: pineappleEA Date: Thu, 31 Dec 2020 11:02:49 +0100 Subject: [PATCH] early-access version 1269 --- README.md | 2 +- src/common/CMakeLists.txt | 3 - src/common/common_funcs.h | 8 - src/core/CMakeLists.txt | 16 +- src/core/core_timing.cpp | 1 - src/core/hle/kernel/client_port.cpp | 3 + src/core/hle/kernel/client_session.cpp | 11 +- src/core/hle/kernel/client_session.h | 8 +- src/core/hle/kernel/errors.h | 3 - src/core/hle/kernel/k_scheduler.cpp | 37 +- src/core/hle/kernel/k_scheduler.h | 5 +- src/core/hle/kernel/kernel.cpp | 17 +- src/core/hle/kernel/kernel.h | 7 + src/core/hle/kernel/memory/memory_layout.h | 19 - src/core/hle/kernel/object.h | 5 - src/core/hle/kernel/process.cpp | 67 ++- src/core/hle/kernel/process.h | 64 +-- src/core/hle/kernel/readable_event.cpp | 18 +- src/core/hle/kernel/readable_event.h | 12 +- src/core/hle/kernel/server_port.cpp | 14 +- src/core/hle/kernel/server_port.h | 9 +- src/core/hle/kernel/server_session.cpp | 23 +- src/core/hle/kernel/server_session.h | 12 +- src/core/hle/kernel/session.cpp | 11 +- src/core/hle/kernel/session.h | 8 +- src/core/hle/kernel/svc.cpp | 394 +++++++++------ src/core/hle/kernel/svc_types.h | 12 - src/core/hle/kernel/svc_wrap.h | 47 +- src/core/hle/kernel/thread.cpp | 344 ++++++------- src/core/hle/kernel/thread.h | 477 +++++++++---------- src/core/hle/kernel/time_manager.cpp | 9 +- src/core/hle/service/nfp/nfp.cpp | 6 + src/core/hle/service/nvflinger/nvflinger.cpp | 4 - src/core/hle/service/sm/sm.cpp | 3 + src/input_common/sdl/sdl_impl.cpp | 47 +- src/yuzu/debugger/wait_tree.cpp | 88 ++-- src/yuzu/debugger/wait_tree.h | 17 +- 37 files changed, 1012 insertions(+), 819 deletions(-) diff --git a/README.md b/README.md index 475a2f7cb..313510caa 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ yuzu emulator early access ============= -This is the source code for early-access 1272. +This is the source code for early-access 1269. ## Legal Notice diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 64b8cf883..5c8003eb1 100755 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -123,7 +123,6 @@ add_library(common STATIC hash.h hex_util.cpp hex_util.h - intrusive_red_black_tree.h logging/backend.cpp logging/backend.h logging/filter.cpp @@ -146,7 +145,6 @@ add_library(common STATIC page_table.h param_package.cpp param_package.h - parent_of_member.h quaternion.h ring_buffer.h scm_rev.cpp @@ -171,7 +169,6 @@ add_library(common STATIC time_zone.h timer.cpp timer.h - tree.h uint128.cpp uint128.h uuid.cpp diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index c90978f9c..367b6bf6e 100755 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h @@ -93,14 +93,6 @@ __declspec(dllimport) void __stdcall DebugBreak(void); return static_cast(key) == 0; \ } -/// Evaluates a boolean expression, and returns a result unless that expression is true. -#define R_UNLESS(expr, res) \ - { \ - if (!(expr)) { \ - return res; \ - } \ - } - namespace Common { [[nodiscard]] constexpr u32 MakeMagic(char a, char b, char c, char d) { diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index fbca16015..4b08adb80 100755 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -141,6 +141,8 @@ add_library(core STATIC hardware_interrupt_manager.h hle/ipc.h hle/ipc_helpers.h + hle/kernel/address_arbiter.cpp + hle/kernel/address_arbiter.h hle/kernel/client_port.cpp hle/kernel/client_port.h hle/kernel/client_session.cpp @@ -154,19 +156,13 @@ add_library(core STATIC hle/kernel/handle_table.h hle/kernel/hle_ipc.cpp hle/kernel/hle_ipc.h - hle/kernel/k_address_arbiter.cpp - hle/kernel/k_address_arbiter.h hle/kernel/k_affinity_mask.h - hle/kernel/k_condition_variable.cpp - hle/kernel/k_condition_variable.h hle/kernel/k_priority_queue.h hle/kernel/k_scheduler.cpp hle/kernel/k_scheduler.h hle/kernel/k_scheduler_lock.h hle/kernel/k_scoped_lock.h hle/kernel/k_scoped_scheduler_lock_and_sleep.h - hle/kernel/k_synchronization_object.cpp - hle/kernel/k_synchronization_object.h hle/kernel/kernel.cpp hle/kernel/kernel.h hle/kernel/memory/address_space_info.cpp @@ -186,6 +182,8 @@ add_library(core STATIC hle/kernel/memory/slab_heap.h hle/kernel/memory/system_control.cpp hle/kernel/memory/system_control.h + hle/kernel/mutex.cpp + hle/kernel/mutex.h hle/kernel/object.cpp hle/kernel/object.h hle/kernel/physical_core.cpp @@ -211,10 +209,12 @@ add_library(core STATIC hle/kernel/shared_memory.h hle/kernel/svc.cpp hle/kernel/svc.h - hle/kernel/svc_common.h - hle/kernel/svc_results.h hle/kernel/svc_types.h hle/kernel/svc_wrap.h + hle/kernel/synchronization_object.cpp + hle/kernel/synchronization_object.h + hle/kernel/synchronization.cpp + hle/kernel/synchronization.h hle/kernel/thread.cpp hle/kernel/thread.h hle/kernel/time_manager.cpp diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 874b5673a..e6c8461a5 100755 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -49,7 +49,6 @@ void CoreTiming::ThreadEntry(CoreTiming& instance) { Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh); instance.on_thread_init(); instance.ThreadLoop(); - MicroProfileOnThreadExit(); } void CoreTiming::Initialize(std::function&& on_thread_init_) { diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp index f8f005f15..8aff2227a 100755 --- a/src/core/hle/kernel/client_port.cpp +++ b/src/core/hle/kernel/client_port.cpp @@ -33,6 +33,9 @@ ResultVal> ClientPort::Connect() { server_port->AppendPendingSession(std::move(server)); } + // Wake the threads waiting on the ServerPort + server_port->Signal(); + return MakeResult(std::move(client)); } diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp index e8e52900d..be9eba519 100755 --- a/src/core/hle/kernel/client_session.cpp +++ b/src/core/hle/kernel/client_session.cpp @@ -12,7 +12,7 @@ namespace Kernel { -ClientSession::ClientSession(KernelCore& kernel) : KSynchronizationObject{kernel} {} +ClientSession::ClientSession(KernelCore& kernel) : SynchronizationObject{kernel} {} ClientSession::~ClientSession() { // This destructor will be called automatically when the last ClientSession handle is closed by @@ -22,6 +22,15 @@ ClientSession::~ClientSession() { } } +bool ClientSession::ShouldWait(const Thread* thread) const { + UNIMPLEMENTED(); + return {}; +} + +void ClientSession::Acquire(Thread* thread) { + UNIMPLEMENTED(); +} + bool ClientSession::IsSignaled() const { UNIMPLEMENTED(); return true; diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h index d5c9ebee8..e5e0690c2 100755 --- a/src/core/hle/kernel/client_session.h +++ b/src/core/hle/kernel/client_session.h @@ -7,7 +7,7 @@ #include #include -#include "core/hle/kernel/k_synchronization_object.h" +#include "core/hle/kernel/synchronization_object.h" #include "core/hle/result.h" union ResultCode; @@ -26,7 +26,7 @@ class KernelCore; class Session; class Thread; -class ClientSession final : public KSynchronizationObject { +class ClientSession final : public SynchronizationObject { public: explicit ClientSession(KernelCore& kernel); ~ClientSession() override; @@ -49,6 +49,10 @@ public: ResultCode SendSyncRequest(std::shared_ptr thread, Core::Memory::Memory& memory, Core::Timing::CoreTiming& core_timing); + bool ShouldWait(const Thread* thread) const override; + + void Acquire(Thread* thread) override; + bool IsSignaled() const override; private: diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h index 7d32a39f0..d4e5d88cf 100755 --- a/src/core/hle/kernel/errors.h +++ b/src/core/hle/kernel/errors.h @@ -13,14 +13,12 @@ namespace Kernel { constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7}; constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14}; constexpr ResultCode ERR_THREAD_TERMINATING{ErrorModule::Kernel, 59}; -constexpr ResultCode ERR_TERMINATION_REQUESTED{ErrorModule::Kernel, 59}; constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101}; constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102}; constexpr ResultCode ERR_OUT_OF_RESOURCES{ErrorModule::Kernel, 103}; constexpr ResultCode ERR_OUT_OF_MEMORY{ErrorModule::Kernel, 104}; constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105}; constexpr ResultCode ERR_INVALID_ADDRESS_STATE{ErrorModule::Kernel, 106}; -constexpr ResultCode ERR_INVALID_CURRENT_MEMORY{ErrorModule::Kernel, 106}; constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS{ErrorModule::Kernel, 108}; constexpr ResultCode ERR_INVALID_MEMORY_RANGE{ErrorModule::Kernel, 110}; constexpr ResultCode ERR_INVALID_PROCESSOR_ID{ErrorModule::Kernel, 113}; @@ -30,7 +28,6 @@ constexpr ResultCode ERR_INVALID_POINTER{ErrorModule::Kernel, 115}; constexpr ResultCode ERR_INVALID_COMBINATION{ErrorModule::Kernel, 116}; constexpr ResultCode RESULT_TIMEOUT{ErrorModule::Kernel, 117}; constexpr ResultCode ERR_SYNCHRONIZATION_CANCELED{ErrorModule::Kernel, 118}; -constexpr ResultCode ERR_CANCELLED{ErrorModule::Kernel, 118}; constexpr ResultCode ERR_OUT_OF_RANGE{ErrorModule::Kernel, 119}; constexpr ResultCode ERR_INVALID_ENUM_VALUE{ErrorModule::Kernel, 120}; constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121}; diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index 42f0ea483..c5fd82a6b 100755 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp @@ -180,22 +180,22 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) { return cores_needing_scheduling; } -void KScheduler::OnThreadStateChanged(KernelCore& kernel, Thread* thread, ThreadState old_state) { +void KScheduler::OnThreadStateChanged(KernelCore& kernel, Thread* thread, u32 old_state) { ASSERT(kernel.GlobalSchedulerContext().IsLocked()); // Check if the state has changed, because if it hasn't there's nothing to do. - const auto cur_state = thread->GetRawState(); + const auto cur_state = thread->scheduling_state; if (cur_state == old_state) { return; } // Update the priority queues. - if (old_state == ThreadState::Runnable) { + if (old_state == static_cast(ThreadSchedStatus::Runnable)) { // If we were previously runnable, then we're not runnable now, and we should remove. GetPriorityQueue(kernel).Remove(thread); IncrementScheduledCount(thread); SetSchedulerUpdateNeeded(kernel); - } else if (cur_state == ThreadState::Runnable) { + } else if (cur_state == static_cast(ThreadSchedStatus::Runnable)) { // If we're now runnable, then we weren't previously, and we should add. GetPriorityQueue(kernel).PushBack(thread); IncrementScheduledCount(thread); @@ -203,11 +203,13 @@ void KScheduler::OnThreadStateChanged(KernelCore& kernel, Thread* thread, Thread } } -void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, s32 old_priority) { +void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, Thread* current_thread, + u32 old_priority) { + ASSERT(kernel.GlobalSchedulerContext().IsLocked()); // If the thread is runnable, we want to change its priority in the queue. - if (thread->GetRawState() == ThreadState::Runnable) { + if (thread->scheduling_state == static_cast(ThreadSchedStatus::Runnable)) { GetPriorityQueue(kernel).ChangePriority( old_priority, thread == kernel.CurrentScheduler()->GetCurrentThread(), thread); IncrementScheduledCount(thread); @@ -220,7 +222,7 @@ void KScheduler::OnThreadAffinityMaskChanged(KernelCore& kernel, Thread* thread, ASSERT(kernel.GlobalSchedulerContext().IsLocked()); // If the thread is runnable, we want to change its affinity in the queue. - if (thread->GetRawState() == ThreadState::Runnable) { + if (thread->scheduling_state == static_cast(ThreadSchedStatus::Runnable)) { GetPriorityQueue(kernel).ChangeAffinityMask(old_core, old_affinity, thread); IncrementScheduledCount(thread); SetSchedulerUpdateNeeded(kernel); @@ -290,7 +292,7 @@ void KScheduler::RotateScheduledQueue(s32 core_id, s32 priority) { // If the best thread we can choose has a priority the same or worse than ours, try to // migrate a higher priority thread. - if (best_thread != nullptr && best_thread->GetPriority() >= priority) { + if (best_thread != nullptr && best_thread->GetPriority() >= static_cast(priority)) { Thread* suggested = priority_queue.GetSuggestedFront(core_id); while (suggested != nullptr) { // If the suggestion's priority is the same as ours, don't bother. @@ -393,8 +395,8 @@ void KScheduler::YieldWithoutCoreMigration() { { KScopedSchedulerLock lock(kernel); - const auto cur_state = cur_thread.GetRawState(); - if (cur_state == ThreadState::Runnable) { + const auto cur_state = cur_thread.scheduling_state; + if (cur_state == static_cast(ThreadSchedStatus::Runnable)) { // Put the current thread at the back of the queue. Thread* next_thread = priority_queue.MoveToScheduledBack(std::addressof(cur_thread)); IncrementScheduledCount(std::addressof(cur_thread)); @@ -434,8 +436,8 @@ void KScheduler::YieldWithCoreMigration() { { KScopedSchedulerLock lock(kernel); - const auto cur_state = cur_thread.GetRawState(); - if (cur_state == ThreadState::Runnable) { + const auto cur_state = cur_thread.scheduling_state; + if (cur_state == static_cast(ThreadSchedStatus::Runnable)) { // Get the current active core. const s32 core_id = cur_thread.GetActiveCore(); @@ -524,8 +526,8 @@ void KScheduler::YieldToAnyThread() { { KScopedSchedulerLock lock(kernel); - const auto cur_state = cur_thread.GetRawState(); - if (cur_state == ThreadState::Runnable) { + const auto cur_state = cur_thread.scheduling_state; + if (cur_state == static_cast(ThreadSchedStatus::Runnable)) { // Get the current active core. const s32 core_id = cur_thread.GetActiveCore(); @@ -643,7 +645,8 @@ void KScheduler::Unload(Thread* thread) { void KScheduler::Reload(Thread* thread) { if (thread) { - ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable."); + ASSERT_MSG(thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable, + "Thread must be runnable."); // Cancel any outstanding wakeup events for this thread thread->SetIsRunning(true); @@ -722,7 +725,7 @@ void KScheduler::SwitchToCurrent() { do { if (current_thread != nullptr && !current_thread->IsHLEThread()) { current_thread->context_guard.lock(); - if (current_thread->GetRawState() != ThreadState::Runnable) { + if (!current_thread->IsRunnable()) { current_thread->context_guard.unlock(); break; } @@ -769,7 +772,7 @@ void KScheduler::Initialize() { { KScopedSchedulerLock lock{system.Kernel()}; - idle_thread->SetState(ThreadState::Runnable); + idle_thread->SetStatus(ThreadStatus::Ready); } } diff --git a/src/core/hle/kernel/k_scheduler.h b/src/core/hle/kernel/k_scheduler.h index 783665123..e84abc84c 100755 --- a/src/core/hle/kernel/k_scheduler.h +++ b/src/core/hle/kernel/k_scheduler.h @@ -100,10 +100,11 @@ public: void YieldToAnyThread(); /// Notify the scheduler a thread's status has changed. - static void OnThreadStateChanged(KernelCore& kernel, Thread* thread, ThreadState old_state); + static void OnThreadStateChanged(KernelCore& kernel, Thread* thread, u32 old_state); /// Notify the scheduler a thread's priority has changed. - static void OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, s32 old_priority); + static void OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, Thread* current_thread, + u32 old_priority); /// Notify the scheduler a thread's core and/or affinity mask has changed. static void OnThreadAffinityMaskChanged(KernelCore& kernel, Thread* thread, diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 8d03f16fb..e8ece8164 100755 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -38,6 +38,7 @@ #include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/service_thread.h" #include "core/hle/kernel/shared_memory.h" +#include "core/hle/kernel/synchronization.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/time_manager.h" #include "core/hle/lock.h" @@ -50,7 +51,8 @@ namespace Kernel { struct KernelCore::Impl { explicit Impl(Core::System& system, KernelCore& kernel) - : time_manager{system}, global_handle_table{kernel}, system{system} {} + : synchronization{system}, time_manager{system}, global_handle_table{kernel}, system{ + system} {} void SetMulticore(bool is_multicore) { this->is_multicore = is_multicore; @@ -305,6 +307,7 @@ struct KernelCore::Impl { std::vector> process_list; Process* current_process = nullptr; std::unique_ptr global_scheduler_context; + Kernel::Synchronization synchronization; Kernel::TimeManager time_manager; std::shared_ptr system_resource_limit; @@ -458,6 +461,14 @@ const std::array& Kern return impl->interrupts; } +Kernel::Synchronization& KernelCore::Synchronization() { + return impl->synchronization; +} + +const Kernel::Synchronization& KernelCore::Synchronization() const { + return impl->synchronization; +} + Kernel::TimeManager& KernelCore::TimeManager() { return impl->time_manager; } @@ -602,9 +613,9 @@ void KernelCore::Suspend(bool in_suspention) { const bool should_suspend = exception_exited || in_suspention; { KScopedSchedulerLock lock(*this); - const auto state = should_suspend ? ThreadState::Runnable : ThreadState::Waiting; + ThreadStatus status = should_suspend ? ThreadStatus::Ready : ThreadStatus::WaitSleep; for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { - impl->suspend_threads[i]->SetState(state); + impl->suspend_threads[i]->SetStatus(status); } } } diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 933d9a7d6..e3169f5a7 100755 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -33,6 +33,7 @@ template class SlabHeap; } // namespace Memory +class AddressArbiter; class ClientPort; class GlobalSchedulerContext; class HandleTable; @@ -128,6 +129,12 @@ public: /// Gets the an instance of the current physical CPU core. const Kernel::PhysicalCore& CurrentPhysicalCore() const; + /// Gets the an instance of the Synchronization Interface. + Kernel::Synchronization& Synchronization(); + + /// Gets the an instance of the Synchronization Interface. + const Kernel::Synchronization& Synchronization() const; + /// Gets the an instance of the TimeManager Interface. Kernel::TimeManager& TimeManager(); diff --git a/src/core/hle/kernel/memory/memory_layout.h b/src/core/hle/kernel/memory/memory_layout.h index c7c0b2f49..9b3d6267a 100755 --- a/src/core/hle/kernel/memory/memory_layout.h +++ b/src/core/hle/kernel/memory/memory_layout.h @@ -5,28 +5,9 @@ #pragma once #include "common/common_types.h" -#include "core/device_memory.h" namespace Kernel::Memory { -constexpr std::size_t KernelAslrAlignment = 2 * 1024 * 1024; -constexpr std::size_t KernelVirtualAddressSpaceWidth = 1ULL << 39; -constexpr std::size_t KernelPhysicalAddressSpaceWidth = 1ULL << 48; -constexpr std::size_t KernelVirtualAddressSpaceBase = 0ULL - KernelVirtualAddressSpaceWidth; -constexpr std::size_t KernelVirtualAddressSpaceEnd = - KernelVirtualAddressSpaceBase + (KernelVirtualAddressSpaceWidth - KernelAslrAlignment); -constexpr std::size_t KernelVirtualAddressSpaceLast = KernelVirtualAddressSpaceEnd - 1; -constexpr std::size_t KernelVirtualAddressSpaceSize = - KernelVirtualAddressSpaceEnd - KernelVirtualAddressSpaceBase; - -constexpr bool IsKernelAddressKey(VAddr key) { - return KernelVirtualAddressSpaceBase <= key && key <= KernelVirtualAddressSpaceLast; -} - -constexpr bool IsKernelAddress(VAddr address) { - return KernelVirtualAddressSpaceBase <= address && address < KernelVirtualAddressSpaceEnd; -} - class MemoryRegion final { friend class MemoryLayout; diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h index 27124ef67..e3391e2af 100755 --- a/src/core/hle/kernel/object.h +++ b/src/core/hle/kernel/object.h @@ -50,11 +50,6 @@ public: } virtual HandleType GetHandleType() const = 0; - void Close() { - // TODO(bunnei): This is a placeholder to decrement the reference count, which we will use - // when we implement KAutoObject instead of using shared_ptr. - } - /** * Check if a thread can wait on the object * @return True if a thread can wait on the object, otherwise false diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 37b77fa6e..b905b486a 100755 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -55,7 +55,7 @@ void SetupMainThread(Core::System& system, Process& owner_process, u32 priority, // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires { KScopedSchedulerLock lock{kernel}; - thread->SetState(ThreadState::Runnable); + thread->SetStatus(ThreadStatus::Ready); } } } // Anonymous namespace @@ -162,6 +162,48 @@ u64 Process::GetTotalPhysicalMemoryUsedWithoutSystemResource() const { return GetTotalPhysicalMemoryUsed() - GetSystemResourceUsage(); } +void Process::InsertConditionVariableThread(std::shared_ptr thread) { + VAddr cond_var_addr = thread->GetCondVarWaitAddress(); + std::list>& thread_list = cond_var_threads[cond_var_addr]; + auto it = thread_list.begin(); + while (it != thread_list.end()) { + const std::shared_ptr current_thread = *it; + if (current_thread->GetPriority() > thread->GetPriority()) { + thread_list.insert(it, thread); + return; + } + ++it; + } + thread_list.push_back(thread); +} + +void Process::RemoveConditionVariableThread(std::shared_ptr thread) { + VAddr cond_var_addr = thread->GetCondVarWaitAddress(); + std::list>& thread_list = cond_var_threads[cond_var_addr]; + auto it = thread_list.begin(); + while (it != thread_list.end()) { + const std::shared_ptr current_thread = *it; + if (current_thread.get() == thread.get()) { + thread_list.erase(it); + return; + } + ++it; + } +} + +std::vector> Process::GetConditionVariableThreads( + const VAddr cond_var_addr) { + std::vector> result{}; + std::list>& thread_list = cond_var_threads[cond_var_addr]; + auto it = thread_list.begin(); + while (it != thread_list.end()) { + std::shared_ptr current_thread = *it; + result.push_back(current_thread); + ++it; + } + return result; +} + void Process::RegisterThread(const Thread* thread) { thread_list.push_back(thread); } @@ -276,7 +318,7 @@ void Process::PrepareForTermination() { continue; // TODO(Subv): When are the other running/ready threads terminated? - ASSERT_MSG(thread->GetState() == ThreadState::Waiting, + ASSERT_MSG(thread->GetStatus() == ThreadStatus::WaitSynch, "Exiting processes with non-waiting threads is currently unimplemented"); thread->Stop(); @@ -364,18 +406,21 @@ void Process::LoadModule(CodeSet code_set, VAddr base_addr) { ReprotectSegment(code_set.DataSegment(), Memory::MemoryPermission::ReadAndWrite); } -bool Process::IsSignaled() const { - ASSERT(kernel.GlobalSchedulerContext().IsLocked()); - return is_signaled; -} - Process::Process(Core::System& system) - : KSynchronizationObject{system.Kernel()}, - page_table{std::make_unique(system)}, handle_table{system.Kernel()}, - address_arbiter{system}, condition_var{system}, system{system} {} + : SynchronizationObject{system.Kernel()}, page_table{std::make_unique( + system)}, + handle_table{system.Kernel()}, address_arbiter{system}, mutex{system}, system{system} {} Process::~Process() = default; +void Process::Acquire(Thread* thread) { + ASSERT_MSG(!ShouldWait(thread), "Object unavailable!"); +} + +bool Process::ShouldWait(const Thread* thread) const { + return !is_signaled; +} + void Process::ChangeStatus(ProcessStatus new_status) { if (status == new_status) { return; @@ -383,7 +428,7 @@ void Process::ChangeStatus(ProcessStatus new_status) { status = new_status; is_signaled = true; - NotifyAvailable(); + Signal(); } ResultCode Process::AllocateMainThreadStack(std::size_t stack_size) { diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 21b6f9bd9..e412e58aa 100755 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -11,11 +11,11 @@ #include #include #include "common/common_types.h" +#include "core/hle/kernel/address_arbiter.h" #include "core/hle/kernel/handle_table.h" -#include "core/hle/kernel/k_address_arbiter.h" -#include "core/hle/kernel/k_condition_variable.h" -#include "core/hle/kernel/k_synchronization_object.h" +#include "core/hle/kernel/mutex.h" #include "core/hle/kernel/process_capability.h" +#include "core/hle/kernel/synchronization_object.h" #include "core/hle/result.h" namespace Core { @@ -63,7 +63,7 @@ enum class ProcessStatus { DebugBreak, }; -class Process final : public KSynchronizationObject { +class Process final : public SynchronizationObject { public: explicit Process(Core::System& system); ~Process() override; @@ -123,30 +123,24 @@ public: return handle_table; } - ResultCode SignalToAddress(VAddr address) { - return condition_var.SignalToAddress(address); + /// Gets a reference to the process' address arbiter. + AddressArbiter& GetAddressArbiter() { + return address_arbiter; } - ResultCode WaitForAddress(Handle handle, VAddr address, u32 tag) { - return condition_var.WaitForAddress(handle, address, tag); + /// Gets a const reference to the process' address arbiter. + const AddressArbiter& GetAddressArbiter() const { + return address_arbiter; } - void SignalConditionVariable(u64 cv_key, int32_t count) { - return condition_var.Signal(cv_key, count); + /// Gets a reference to the process' mutex lock. + Mutex& GetMutex() { + return mutex; } - ResultCode WaitConditionVariable(VAddr address, u64 cv_key, u32 tag, s64 ns) { - return condition_var.Wait(address, cv_key, tag, ns); - } - - ResultCode SignalAddressArbiter(VAddr address, Svc::SignalType signal_type, s32 value, - s32 count) { - return address_arbiter.SignalToAddress(address, signal_type, value, count); - } - - ResultCode WaitAddressArbiter(VAddr address, Svc::ArbitrationType arb_type, s32 value, - s64 timeout) { - return address_arbiter.WaitForAddress(address, arb_type, value, timeout); + /// Gets a const reference to the process' mutex lock + const Mutex& GetMutex() const { + return mutex; } /// Gets the address to the process' dedicated TLS region. @@ -256,6 +250,15 @@ public: return thread_list; } + /// Insert a thread into the condition variable wait container + void InsertConditionVariableThread(std::shared_ptr thread); + + /// Remove a thread from the condition variable wait container + void RemoveConditionVariableThread(std::shared_ptr thread); + + /// Obtain all condition variable threads waiting for some address + std::vector> GetConditionVariableThreads(VAddr cond_var_addr); + /// Registers a thread as being created under this process, /// adding it to this process' thread list. void RegisterThread(const Thread* thread); @@ -301,8 +304,6 @@ public: void LoadModule(CodeSet code_set, VAddr base_addr); - virtual bool IsSignaled() const override; - /////////////////////////////////////////////////////////////////////////////////////////////// // Thread-local storage management @@ -313,6 +314,12 @@ public: void FreeTLSRegion(VAddr tls_address); private: + /// Checks if the specified thread should wait until this process is available. + bool ShouldWait(const Thread* thread) const override; + + /// Acquires/locks this process for the specified thread if it's available. + void Acquire(Thread* thread) override; + /// Changes the process status. If the status is different /// from the current process status, then this will trigger /// a process signal. @@ -366,12 +373,12 @@ private: HandleTable handle_table; /// Per-process address arbiter. - KAddressArbiter address_arbiter; + AddressArbiter address_arbiter; /// The per-process mutex lock instance used for handling various /// forms of services, such as lock arbitration, and condition /// variable related facilities. - KConditionVariable condition_var; + Mutex mutex; /// Address indicating the location of the process' dedicated TLS region. VAddr tls_region_address = 0; @@ -382,6 +389,9 @@ private: /// List of threads that are running with this process as their owner. std::list thread_list; + /// List of threads waiting for a condition variable + std::unordered_map>> cond_var_threads; + /// Address of the top of the main thread's stack VAddr main_thread_stack_top{}; @@ -400,8 +410,6 @@ private: /// Schedule count of this process s64 schedule_count{}; - bool is_signaled{}; - /// System context Core::System& system; }; diff --git a/src/core/hle/kernel/readable_event.cpp b/src/core/hle/kernel/readable_event.cpp index 99ed0857e..cea262ce0 100755 --- a/src/core/hle/kernel/readable_event.cpp +++ b/src/core/hle/kernel/readable_event.cpp @@ -14,22 +14,24 @@ namespace Kernel { -ReadableEvent::ReadableEvent(KernelCore& kernel) : KSynchronizationObject{kernel} {} +ReadableEvent::ReadableEvent(KernelCore& kernel) : SynchronizationObject{kernel} {} ReadableEvent::~ReadableEvent() = default; +bool ReadableEvent::ShouldWait(const Thread* thread) const { + return !is_signaled; +} + +void ReadableEvent::Acquire(Thread* thread) { + ASSERT_MSG(IsSignaled(), "object unavailable!"); +} + void ReadableEvent::Signal() { if (is_signaled) { return; } is_signaled = true; - NotifyAvailable(); -} - -bool ReadableEvent::IsSignaled() const { - ASSERT(kernel.GlobalSchedulerContext().IsLocked()); - - return is_signaled; + SynchronizationObject::Signal(); } void ReadableEvent::Clear() { diff --git a/src/core/hle/kernel/readable_event.h b/src/core/hle/kernel/readable_event.h index 95894db8d..3264dd066 100755 --- a/src/core/hle/kernel/readable_event.h +++ b/src/core/hle/kernel/readable_event.h @@ -4,8 +4,8 @@ #pragma once -#include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/kernel/object.h" +#include "core/hle/kernel/synchronization_object.h" union ResultCode; @@ -14,7 +14,7 @@ namespace Kernel { class KernelCore; class WritableEvent; -class ReadableEvent final : public KSynchronizationObject { +class ReadableEvent final : public SynchronizationObject { friend class WritableEvent; public: @@ -32,6 +32,9 @@ public: return HANDLE_TYPE; } + bool ShouldWait(const Thread* thread) const override; + void Acquire(Thread* thread) override; + /// Unconditionally clears the readable event's state. void Clear(); @@ -43,14 +46,11 @@ public: /// then ERR_INVALID_STATE will be returned. ResultCode Reset(); - void Signal(); - - virtual bool IsSignaled() const override; + void Signal() override; private: explicit ReadableEvent(KernelCore& kernel); - bool is_signaled{}; std::string name; ///< Name of event (optional) }; diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp index 82857f93b..a549ae9d7 100755 --- a/src/core/hle/kernel/server_port.cpp +++ b/src/core/hle/kernel/server_port.cpp @@ -13,7 +13,7 @@ namespace Kernel { -ServerPort::ServerPort(KernelCore& kernel) : KSynchronizationObject{kernel} {} +ServerPort::ServerPort(KernelCore& kernel) : SynchronizationObject{kernel} {} ServerPort::~ServerPort() = default; ResultVal> ServerPort::Accept() { @@ -28,9 +28,15 @@ ResultVal> ServerPort::Accept() { void ServerPort::AppendPendingSession(std::shared_ptr pending_session) { pending_sessions.push_back(std::move(pending_session)); - if (pending_sessions.size() == 1) { - NotifyAvailable(); - } +} + +bool ServerPort::ShouldWait(const Thread* thread) const { + // If there are no pending sessions, we wait until a new one is added. + return pending_sessions.empty(); +} + +void ServerPort::Acquire(Thread* thread) { + ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); } bool ServerPort::IsSignaled() const { diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h index 587d01e0a..41b191b86 100755 --- a/src/core/hle/kernel/server_port.h +++ b/src/core/hle/kernel/server_port.h @@ -9,8 +9,8 @@ #include #include #include "common/common_types.h" -#include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/kernel/object.h" +#include "core/hle/kernel/synchronization_object.h" #include "core/hle/result.h" namespace Kernel { @@ -20,7 +20,7 @@ class KernelCore; class ServerSession; class SessionRequestHandler; -class ServerPort final : public KSynchronizationObject { +class ServerPort final : public SynchronizationObject { public: explicit ServerPort(KernelCore& kernel); ~ServerPort() override; @@ -79,7 +79,10 @@ public: /// waiting to be accepted by this port. void AppendPendingSession(std::shared_ptr pending_session); - virtual bool IsSignaled() const override; + bool ShouldWait(const Thread* thread) const override; + void Acquire(Thread* thread) override; + + bool IsSignaled() const override; private: /// ServerSessions waiting to be accepted by the port diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 4f2bb7822..b40fe3916 100755 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -24,7 +24,7 @@ namespace Kernel { -ServerSession::ServerSession(KernelCore& kernel) : KSynchronizationObject{kernel} {} +ServerSession::ServerSession(KernelCore& kernel) : SynchronizationObject{kernel} {} ServerSession::~ServerSession() { kernel.ReleaseServiceThread(service_thread); @@ -42,6 +42,16 @@ ResultVal> ServerSession::Create(KernelCore& kern return MakeResult(std::move(session)); } +bool ServerSession::ShouldWait(const Thread* thread) const { + // Closed sessions should never wait, an error will be returned from svcReplyAndReceive. + if (!parent->Client()) { + return false; + } + + // Wait if we have no pending requests, or if we're currently handling a request. + return pending_requesting_threads.empty() || currently_handling != nullptr; +} + bool ServerSession::IsSignaled() const { // Closed sessions should never wait, an error will be returned from svcReplyAndReceive. if (!parent->Client()) { @@ -52,6 +62,15 @@ bool ServerSession::IsSignaled() const { return !pending_requesting_threads.empty() && currently_handling == nullptr; } +void ServerSession::Acquire(Thread* thread) { + ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); + // We are now handling a request, pop it from the stack. + // TODO(Subv): What happens if the client endpoint is closed before any requests are made? + ASSERT(!pending_requesting_threads.empty()); + currently_handling = pending_requesting_threads.back(); + pending_requesting_threads.pop_back(); +} + void ServerSession::ClientDisconnected() { // We keep a shared pointer to the hle handler to keep it alive throughout // the call to ClientDisconnected, as ClientDisconnected invalidates the @@ -153,7 +172,7 @@ ResultCode ServerSession::CompleteSyncRequest(HLERequestContext& context) { { KScopedSchedulerLock lock(kernel); if (!context.IsThreadWaiting()) { - context.GetThread().Wakeup(); + context.GetThread().ResumeFromWait(); context.GetThread().SetSynchronizationResults(nullptr, result); } } diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index bfeb05c8d..e8d1d99ea 100755 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h @@ -10,8 +10,8 @@ #include #include "common/threadsafe_queue.h" -#include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/kernel/service_thread.h" +#include "core/hle/kernel/synchronization_object.h" #include "core/hle/result.h" namespace Core::Memory { @@ -43,7 +43,7 @@ class Thread; * After the server replies to the request, the response is marshalled back to the caller's * TLS buffer and control is transferred back to it. */ -class ServerSession final : public KSynchronizationObject { +class ServerSession final : public SynchronizationObject { friend class ServiceThread; public: @@ -77,6 +77,8 @@ public: return parent.get(); } + bool IsSignaled() const override; + /** * Sets the HLE handler for the session. This handler will be called to service IPC requests * instead of the regular IPC machinery. (The regular IPC machinery is currently not @@ -98,6 +100,10 @@ public: ResultCode HandleSyncRequest(std::shared_ptr thread, Core::Memory::Memory& memory, Core::Timing::CoreTiming& core_timing); + bool ShouldWait(const Thread* thread) const override; + + void Acquire(Thread* thread) override; + /// Called when a client disconnection occurs. void ClientDisconnected(); @@ -124,8 +130,6 @@ public: convert_to_domain = true; } - virtual bool IsSignaled() const override; - private: /// Queues a sync request from the emulated application. ResultCode QueueSyncRequest(std::shared_ptr thread, Core::Memory::Memory& memory); diff --git a/src/core/hle/kernel/session.cpp b/src/core/hle/kernel/session.cpp index 75304b961..e4dd53e24 100755 --- a/src/core/hle/kernel/session.cpp +++ b/src/core/hle/kernel/session.cpp @@ -9,7 +9,7 @@ namespace Kernel { -Session::Session(KernelCore& kernel) : KSynchronizationObject{kernel} {} +Session::Session(KernelCore& kernel) : SynchronizationObject{kernel} {} Session::~Session() = default; Session::SessionPair Session::Create(KernelCore& kernel, std::string name) { @@ -24,9 +24,18 @@ Session::SessionPair Session::Create(KernelCore& kernel, std::string name) { return std::make_pair(std::move(client_session), std::move(server_session)); } +bool Session::ShouldWait(const Thread* thread) const { + UNIMPLEMENTED(); + return {}; +} + bool Session::IsSignaled() const { UNIMPLEMENTED(); return true; } +void Session::Acquire(Thread* thread) { + UNIMPLEMENTED(); +} + } // namespace Kernel diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h index f6dd2c1d2..7cd9c0d77 100755 --- a/src/core/hle/kernel/session.h +++ b/src/core/hle/kernel/session.h @@ -8,7 +8,7 @@ #include #include -#include "core/hle/kernel/k_synchronization_object.h" +#include "core/hle/kernel/synchronization_object.h" namespace Kernel { @@ -19,7 +19,7 @@ class ServerSession; * Parent structure to link the client and server endpoints of a session with their associated * client port. */ -class Session final : public KSynchronizationObject { +class Session final : public SynchronizationObject { public: explicit Session(KernelCore& kernel); ~Session() override; @@ -37,8 +37,12 @@ public: return HANDLE_TYPE; } + bool ShouldWait(const Thread* thread) const override; + bool IsSignaled() const override; + void Acquire(Thread* thread) override; + std::shared_ptr Client() { if (auto result{client.lock()}) { return result; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 99bb4ea20..de3ed25da 100755 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -10,7 +10,6 @@ #include "common/alignment.h" #include "common/assert.h" -#include "common/common_funcs.h" #include "common/fiber.h" #include "common/logging/log.h" #include "common/microprofile.h" @@ -20,28 +19,26 @@ #include "core/core_timing.h" #include "core/core_timing_util.h" #include "core/cpu_manager.h" +#include "core/hle/kernel/address_arbiter.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/handle_table.h" -#include "core/hle/kernel/k_address_arbiter.h" -#include "core/hle/kernel/k_condition_variable.h" #include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" -#include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/memory/memory_block.h" -#include "core/hle/kernel/memory/memory_layout.h" #include "core/hle/kernel/memory/page_table.h" +#include "core/hle/kernel/mutex.h" #include "core/hle/kernel/physical_core.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/kernel/svc.h" -#include "core/hle/kernel/svc_results.h" #include "core/hle/kernel/svc_types.h" #include "core/hle/kernel/svc_wrap.h" +#include "core/hle/kernel/synchronization.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/time_manager.h" #include "core/hle/kernel/transfer_memory.h" @@ -346,10 +343,27 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) { auto thread = kernel.CurrentScheduler()->GetCurrentThread(); { KScopedSchedulerLock lock(kernel); - thread->SetState(ThreadState::Waiting); + thread->InvalidateHLECallback(); + thread->SetStatus(ThreadStatus::WaitIPC); session->SendSyncRequest(SharedFrom(thread), system.Memory(), system.CoreTiming()); } + if (thread->HasHLECallback()) { + Handle event_handle = thread->GetHLETimeEvent(); + if (event_handle != InvalidHandle) { + auto& time_manager = kernel.TimeManager(); + time_manager.UnscheduleTimeEvent(event_handle); + } + + { + KScopedSchedulerLock lock(kernel); + auto* sync_object = thread->GetHLESyncObject(); + sync_object->RemoveWaitingThread(SharedFrom(thread)); + } + + thread->InvokeHLECallback(SharedFrom(thread)); + } + return thread->GetSignalingResult(); } @@ -422,7 +436,7 @@ static ResultCode GetProcessId32(Core::System& system, u32* process_id_low, u32* } /// Wait for the given handles to synchronize, timeout after the specified nanoseconds -static ResultCode WaitSynchronization(Core::System& system, s32* index, VAddr handles_address, +static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr handles_address, u64 handle_count, s64 nano_seconds) { LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}", handles_address, handle_count, nano_seconds); @@ -444,26 +458,28 @@ static ResultCode WaitSynchronization(Core::System& system, s32* index, VAddr ha } auto& kernel = system.Kernel(); - std::vector objects(handle_count); + Thread::ThreadSynchronizationObjects objects(handle_count); const auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); for (u64 i = 0; i < handle_count; ++i) { const Handle handle = memory.Read32(handles_address + i * sizeof(Handle)); - const auto object = handle_table.Get(handle); + const auto object = handle_table.Get(handle); if (object == nullptr) { LOG_ERROR(Kernel_SVC, "Object is a nullptr"); return ERR_INVALID_HANDLE; } - objects[i] = object.get(); + objects[i] = object; } - return KSynchronizationObject::Wait(kernel, index, objects.data(), - static_cast(objects.size()), nano_seconds); + auto& synchronization = kernel.Synchronization(); + const auto [result, handle_result] = synchronization.WaitFor(objects, nano_seconds); + *index = handle_result; + return result; } static ResultCode WaitSynchronization32(Core::System& system, u32 timeout_low, u32 handles_address, - s32 handle_count, u32 timeout_high, s32* index) { + s32 handle_count, u32 timeout_high, Handle* index) { const s64 nano_seconds{(static_cast(timeout_high) << 32) | static_cast(timeout_low)}; return WaitSynchronization(system, index, handles_address, handle_count, nano_seconds); } @@ -488,37 +504,56 @@ static ResultCode CancelSynchronization32(Core::System& system, Handle thread_ha return CancelSynchronization(system, thread_handle); } -/// Attempts to locks a mutex -static ResultCode ArbitrateLock(Core::System& system, Handle thread_handle, VAddr address, - u32 tag) { - LOG_TRACE(Kernel_SVC, "called thread_handle=0x{:08X}, address=0x{:X}, tag=0x{:08X}", - thread_handle, address, tag); +/// Attempts to locks a mutex, creating it if it does not already exist +static ResultCode ArbitrateLock(Core::System& system, Handle holding_thread_handle, + VAddr mutex_addr, Handle requesting_thread_handle) { + LOG_TRACE(Kernel_SVC, + "called holding_thread_handle=0x{:08X}, mutex_addr=0x{:X}, " + "requesting_current_thread_handle=0x{:08X}", + holding_thread_handle, mutex_addr, requesting_thread_handle); - // Validate the input address. - R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory); - R_UNLESS(Common::IsAligned(address, sizeof(u32)), Svc::ResultInvalidAddress); + if (Core::Memory::IsKernelVirtualAddress(mutex_addr)) { + LOG_ERROR(Kernel_SVC, "Mutex Address is a kernel virtual address, mutex_addr={:016X}", + mutex_addr); + return ERR_INVALID_ADDRESS_STATE; + } - return system.Kernel().CurrentProcess()->WaitForAddress(thread_handle, address, tag); + if (!Common::IsWordAligned(mutex_addr)) { + LOG_ERROR(Kernel_SVC, "Mutex Address is not word aligned, mutex_addr={:016X}", mutex_addr); + return ERR_INVALID_ADDRESS; + } + + auto* const current_process = system.Kernel().CurrentProcess(); + return current_process->GetMutex().TryAcquire(mutex_addr, holding_thread_handle, + requesting_thread_handle); } -static ResultCode ArbitrateLock32(Core::System& system, Handle thread_handle, u32 address, - u32 tag) { - return ArbitrateLock(system, thread_handle, address, tag); +static ResultCode ArbitrateLock32(Core::System& system, Handle holding_thread_handle, + u32 mutex_addr, Handle requesting_thread_handle) { + return ArbitrateLock(system, holding_thread_handle, mutex_addr, requesting_thread_handle); } /// Unlock a mutex -static ResultCode ArbitrateUnlock(Core::System& system, VAddr address) { - LOG_TRACE(Kernel_SVC, "called address=0x{:X}", address); +static ResultCode ArbitrateUnlock(Core::System& system, VAddr mutex_addr) { + LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr); - // Validate the input address. - R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory); - R_UNLESS(Common::IsAligned(address, sizeof(u32)), Svc::ResultInvalidAddress); + if (Core::Memory::IsKernelVirtualAddress(mutex_addr)) { + LOG_ERROR(Kernel_SVC, "Mutex Address is a kernel virtual address, mutex_addr={:016X}", + mutex_addr); + return ERR_INVALID_ADDRESS_STATE; + } - return system.Kernel().CurrentProcess()->SignalToAddress(address); + if (!Common::IsWordAligned(mutex_addr)) { + LOG_ERROR(Kernel_SVC, "Mutex Address is not word aligned, mutex_addr={:016X}", mutex_addr); + return ERR_INVALID_ADDRESS; + } + + auto* const current_process = system.Kernel().CurrentProcess(); + return current_process->GetMutex().Release(mutex_addr); } -static ResultCode ArbitrateUnlock32(Core::System& system, u32 address) { - return ArbitrateUnlock(system, address); +static ResultCode ArbitrateUnlock32(Core::System& system, u32 mutex_addr) { + return ArbitrateUnlock(system, mutex_addr); } enum class BreakType : u32 { @@ -1145,7 +1180,7 @@ static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 pri return ERR_INVALID_HANDLE; } - thread->SetBasePriority(priority); + thread->SetPriority(priority); return RESULT_SUCCESS; } @@ -1524,7 +1559,7 @@ static ResultCode StartThread(Core::System& system, Handle thread_handle) { return ERR_INVALID_HANDLE; } - ASSERT(thread->GetState() == ThreadState::Initialized); + ASSERT(thread->GetStatus() == ThreadStatus::Dormant); return thread->Start(); } @@ -1585,135 +1620,224 @@ static void SleepThread32(Core::System& system, u32 nanoseconds_low, u32 nanosec } /// Wait process wide key atomic -static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr address, VAddr cv_key, - u32 tag, s64 timeout_ns) { - LOG_TRACE(Kernel_SVC, "called address={:X}, cv_key={:X}, tag=0x{:08X}, timeout_ns={}", address, - cv_key, tag, timeout_ns); +static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_addr, + VAddr condition_variable_addr, Handle thread_handle, + s64 nano_seconds) { + LOG_TRACE( + Kernel_SVC, + "called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}", + mutex_addr, condition_variable_addr, thread_handle, nano_seconds); - // Validate input. - R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory); - R_UNLESS(Common::IsAligned(address, sizeof(int32_t)), Svc::ResultInvalidAddress); - - // Convert timeout from nanoseconds to ticks. - s64 timeout{}; - if (timeout_ns > 0) { - const s64 offset_tick(timeout_ns); - if (offset_tick > 0) { - timeout = offset_tick + 2; - if (timeout <= 0) { - timeout = std::numeric_limits::max(); - } - } else { - timeout = std::numeric_limits::max(); - } - } else { - timeout = timeout_ns; + if (Core::Memory::IsKernelVirtualAddress(mutex_addr)) { + LOG_ERROR( + Kernel_SVC, + "Given mutex address must not be within the kernel address space. address=0x{:016X}", + mutex_addr); + return ERR_INVALID_ADDRESS_STATE; } - // Wait on the condition variable. - return system.Kernel().CurrentProcess()->WaitConditionVariable( - address, Common::AlignDown(cv_key, sizeof(u32)), tag, timeout); + if (!Common::IsWordAligned(mutex_addr)) { + LOG_ERROR(Kernel_SVC, "Given mutex address must be word-aligned. address=0x{:016X}", + mutex_addr); + return ERR_INVALID_ADDRESS; + } + + ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4)); + auto& kernel = system.Kernel(); + Handle event_handle; + Thread* current_thread = kernel.CurrentScheduler()->GetCurrentThread(); + auto* const current_process = kernel.CurrentProcess(); + { + KScopedSchedulerLockAndSleep lock(kernel, event_handle, current_thread, nano_seconds); + const auto& handle_table = current_process->GetHandleTable(); + std::shared_ptr thread = handle_table.Get(thread_handle); + ASSERT(thread); + + current_thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT); + + if (thread->IsPendingTermination()) { + lock.CancelSleep(); + return ERR_THREAD_TERMINATING; + } + + const auto release_result = current_process->GetMutex().Release(mutex_addr); + if (release_result.IsError()) { + lock.CancelSleep(); + return release_result; + } + + if (nano_seconds == 0) { + lock.CancelSleep(); + return RESULT_TIMEOUT; + } + + current_thread->SetCondVarWaitAddress(condition_variable_addr); + current_thread->SetMutexWaitAddress(mutex_addr); + current_thread->SetWaitHandle(thread_handle); + current_thread->SetStatus(ThreadStatus::WaitCondVar); + current_process->InsertConditionVariableThread(SharedFrom(current_thread)); + } + + if (event_handle != InvalidHandle) { + auto& time_manager = kernel.TimeManager(); + time_manager.UnscheduleTimeEvent(event_handle); + } + + { + KScopedSchedulerLock lock(kernel); + + auto* owner = current_thread->GetLockOwner(); + if (owner != nullptr) { + owner->RemoveMutexWaiter(SharedFrom(current_thread)); + } + + current_process->RemoveConditionVariableThread(SharedFrom(current_thread)); + } + // Note: Deliberately don't attempt to inherit the lock owner's priority. + + return current_thread->GetSignalingResult(); } -static ResultCode WaitProcessWideKeyAtomic32(Core::System& system, u32 address, u32 cv_key, u32 tag, - u32 timeout_ns_low, u32 timeout_ns_high) { - const auto timeout_ns = static_cast(timeout_ns_low | (u64{timeout_ns_high} << 32)); - return WaitProcessWideKeyAtomic(system, address, cv_key, tag, timeout_ns); +static ResultCode WaitProcessWideKeyAtomic32(Core::System& system, u32 mutex_addr, + u32 condition_variable_addr, Handle thread_handle, + u32 nanoseconds_low, u32 nanoseconds_high) { + const auto nanoseconds = static_cast(nanoseconds_low | (u64{nanoseconds_high} << 32)); + return WaitProcessWideKeyAtomic(system, mutex_addr, condition_variable_addr, thread_handle, + nanoseconds); } /// Signal process wide key -static void SignalProcessWideKey(Core::System& system, VAddr cv_key, s32 count) { - LOG_TRACE(Kernel_SVC, "called, cv_key=0x{:X}, count=0x{:08X}", cv_key, count); +static void SignalProcessWideKey(Core::System& system, VAddr condition_variable_addr, s32 target) { + LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}", + condition_variable_addr, target); - // Signal the condition variable. - return system.Kernel().CurrentProcess()->SignalConditionVariable( - Common::AlignDown(cv_key, sizeof(u32)), count); -} + ASSERT(condition_variable_addr == Common::AlignDown(condition_variable_addr, 4)); -static void SignalProcessWideKey32(Core::System& system, u32 cv_key, s32 count) { - SignalProcessWideKey(system, cv_key, count); -} + // Retrieve a list of all threads that are waiting for this condition variable. + auto& kernel = system.Kernel(); + KScopedSchedulerLock lock(kernel); + auto* const current_process = kernel.CurrentProcess(); + std::vector> waiting_threads = + current_process->GetConditionVariableThreads(condition_variable_addr); -namespace { + // Only process up to 'target' threads, unless 'target' is less equal 0, in which case process + // them all. + std::size_t last = waiting_threads.size(); + if (target > 0) { + last = std::min(waiting_threads.size(), static_cast(target)); + } + for (std::size_t index = 0; index < last; ++index) { + auto& thread = waiting_threads[index]; -constexpr bool IsValidSignalType(Svc::SignalType type) { - switch (type) { - case Svc::SignalType::Signal: - case Svc::SignalType::SignalAndIncrementIfEqual: - case Svc::SignalType::SignalAndModifyByWaitingCountIfEqual: - return true; - default: - return false; + ASSERT(thread->GetCondVarWaitAddress() == condition_variable_addr); + + // liberate Cond Var Thread. + current_process->RemoveConditionVariableThread(thread); + + const std::size_t current_core = system.CurrentCoreIndex(); + auto& monitor = system.Monitor(); + + // Atomically read the value of the mutex. + u32 mutex_val = 0; + u32 update_val = 0; + const VAddr mutex_address = thread->GetMutexWaitAddress(); + do { + // If the mutex is not yet acquired, acquire it. + mutex_val = monitor.ExclusiveRead32(current_core, mutex_address); + + if (mutex_val != 0) { + update_val = mutex_val | Mutex::MutexHasWaitersFlag; + } else { + update_val = thread->GetWaitHandle(); + } + } while (!monitor.ExclusiveWrite32(current_core, mutex_address, update_val)); + monitor.ClearExclusive(); + if (mutex_val == 0) { + // We were able to acquire the mutex, resume this thread. + auto* const lock_owner = thread->GetLockOwner(); + if (lock_owner != nullptr) { + lock_owner->RemoveMutexWaiter(thread); + } + + thread->SetLockOwner(nullptr); + thread->SetSynchronizationResults(nullptr, RESULT_SUCCESS); + thread->ResumeFromWait(); + } else { + // The mutex is already owned by some other thread, make this thread wait on it. + const Handle owner_handle = static_cast(mutex_val & Mutex::MutexOwnerMask); + const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); + auto owner = handle_table.Get(owner_handle); + ASSERT(owner); + if (thread->GetStatus() == ThreadStatus::WaitCondVar) { + thread->SetStatus(ThreadStatus::WaitMutex); + } + + owner->AddMutexWaiter(thread); + } } } -constexpr bool IsValidArbitrationType(Svc::ArbitrationType type) { - switch (type) { - case Svc::ArbitrationType::WaitIfLessThan: - case Svc::ArbitrationType::DecrementAndWaitIfLessThan: - case Svc::ArbitrationType::WaitIfEqual: - return true; - default: - return false; - } +static void SignalProcessWideKey32(Core::System& system, u32 condition_variable_addr, s32 target) { + SignalProcessWideKey(system, condition_variable_addr, target); } -} // namespace - // Wait for an address (via Address Arbiter) -static ResultCode WaitForAddress(Core::System& system, VAddr address, Svc::ArbitrationType arb_type, - s32 value, s64 timeout_ns) { - LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, arb_type=0x{:X}, value=0x{:X}, timeout_ns={}", - address, arb_type, value, timeout_ns); +static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type, s32 value, + s64 timeout) { + LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, timeout={}", address, + type, value, timeout); - // Validate input. - R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory); - R_UNLESS(Common::IsAligned(address, sizeof(int32_t)), Svc::ResultInvalidAddress); - R_UNLESS(IsValidArbitrationType(arb_type), Svc::ResultInvalidEnumValue); - - // Convert timeout from nanoseconds to ticks. - s64 timeout{}; - if (timeout_ns > 0) { - const s64 offset_tick(timeout_ns); - if (offset_tick > 0) { - timeout = offset_tick + 2; - if (timeout <= 0) { - timeout = std::numeric_limits::max(); - } - } else { - timeout = std::numeric_limits::max(); - } - } else { - timeout = timeout_ns; + // If the passed address is a kernel virtual address, return invalid memory state. + if (Core::Memory::IsKernelVirtualAddress(address)) { + LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address); + return ERR_INVALID_ADDRESS_STATE; } - return system.Kernel().CurrentProcess()->WaitAddressArbiter(address, arb_type, value, timeout); + // If the address is not properly aligned to 4 bytes, return invalid address. + if (!Common::IsWordAligned(address)) { + LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address); + return ERR_INVALID_ADDRESS; + } + + const auto arbitration_type = static_cast(type); + auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter(); + const ResultCode result = + address_arbiter.WaitForAddress(address, arbitration_type, value, timeout); + return result; } -static ResultCode WaitForAddress32(Core::System& system, u32 address, Svc::ArbitrationType arb_type, - s32 value, u32 timeout_ns_low, u32 timeout_ns_high) { - const auto timeout = static_cast(timeout_ns_low | (u64{timeout_ns_high} << 32)); - return WaitForAddress(system, address, arb_type, value, timeout); +static ResultCode WaitForAddress32(Core::System& system, u32 address, u32 type, s32 value, + u32 timeout_low, u32 timeout_high) { + const auto timeout = static_cast(timeout_low | (u64{timeout_high} << 32)); + return WaitForAddress(system, address, type, value, timeout); } // Signals to an address (via Address Arbiter) -static ResultCode SignalToAddress(Core::System& system, VAddr address, Svc::SignalType signal_type, - s32 value, s32 count) { - LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, signal_type=0x{:X}, value=0x{:X}, count=0x{:X}", - address, signal_type, value, count); +static ResultCode SignalToAddress(Core::System& system, VAddr address, u32 type, s32 value, + s32 num_to_wake) { + LOG_TRACE(Kernel_SVC, "called, address=0x{:X}, type=0x{:X}, value=0x{:X}, num_to_wake=0x{:X}", + address, type, value, num_to_wake); - // Validate input. - R_UNLESS(!Memory::IsKernelAddress(address), Svc::ResultInvalidCurrentMemory); - R_UNLESS(Common::IsAligned(address, sizeof(s32)), Svc::ResultInvalidAddress); - R_UNLESS(IsValidSignalType(signal_type), Svc::ResultInvalidEnumValue); + // If the passed address is a kernel virtual address, return invalid memory state. + if (Core::Memory::IsKernelVirtualAddress(address)) { + LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address); + return ERR_INVALID_ADDRESS_STATE; + } - return system.Kernel().CurrentProcess()->SignalAddressArbiter(address, signal_type, value, - count); + // If the address is not properly aligned to 4 bytes, return invalid address. + if (!Common::IsWordAligned(address)) { + LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address); + return ERR_INVALID_ADDRESS; + } + + const auto signal_type = static_cast(type); + auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter(); + return address_arbiter.SignalToAddress(address, signal_type, value, num_to_wake); } -static ResultCode SignalToAddress32(Core::System& system, u32 address, Svc::SignalType signal_type, - s32 value, s32 count) { - return SignalToAddress(system, address, signal_type, value, count); +static ResultCode SignalToAddress32(Core::System& system, u32 address, u32 type, s32 value, + s32 num_to_wake) { + return SignalToAddress(system, address, type, value, num_to_wake); } static void KernelDebug([[maybe_unused]] Core::System& system, diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h index 356947741..986724beb 100755 --- a/src/core/hle/kernel/svc_types.h +++ b/src/core/hle/kernel/svc_types.h @@ -65,16 +65,4 @@ struct MemoryInfo { u32 padding{}; }; -enum class SignalType : u32 { - Signal = 0, - SignalAndIncrementIfEqual = 1, - SignalAndModifyByWaitingCountIfEqual = 2, -}; - -enum class ArbitrationType : u32 { - WaitIfLessThan = 0, - DecrementAndWaitIfLessThan = 1, - WaitIfEqual = 2, -}; - } // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index a32750ed7..0b6dd9df0 100755 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -7,7 +7,6 @@ #include "common/common_types.h" #include "core/arm/arm_interface.h" #include "core/core.h" -#include "core/hle/kernel/svc_types.h" #include "core/hle/result.h" namespace Kernel { @@ -216,10 +215,9 @@ void SvcWrap64(Core::System& system) { func(system, static_cast(Param(system, 0)), Param(system, 1), Param(system, 2)).raw); } -// Used by WaitSynchronization -template +template void SvcWrap64(Core::System& system) { - s32 param_1 = 0; + u32 param_1 = 0; const u32 retval = func(system, ¶m_1, Param(system, 1), static_cast(Param(system, 2)), static_cast(Param(system, 3))) .raw; @@ -278,22 +276,18 @@ void SvcWrap64(Core::System& system) { FuncReturn(system, retval); } -// Used by WaitForAddress -template +template void SvcWrap64(Core::System& system) { - FuncReturn(system, - func(system, Param(system, 0), static_cast(Param(system, 1)), - static_cast(Param(system, 2)), static_cast(Param(system, 3))) - .raw); + FuncReturn(system, func(system, Param(system, 0), static_cast(Param(system, 1)), + static_cast(Param(system, 2)), static_cast(Param(system, 3))) + .raw); } -// Used by SignalToAddress -template +template void SvcWrap64(Core::System& system) { - FuncReturn(system, - func(system, Param(system, 0), static_cast(Param(system, 1)), - static_cast(Param(system, 2)), static_cast(Param(system, 3))) - .raw); + FuncReturn(system, func(system, Param(system, 0), static_cast(Param(system, 1)), + static_cast(Param(system, 2)), static_cast(Param(system, 3))) + .raw); } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -509,23 +503,22 @@ void SvcWrap32(Core::System& system) { } // Used by WaitForAddress32 -template +template void SvcWrap32(Core::System& system) { const u32 retval = func(system, static_cast(Param(system, 0)), - static_cast(Param(system, 1)), - static_cast(Param(system, 2)), static_cast(Param(system, 3)), - static_cast(Param(system, 4))) + static_cast(Param(system, 1)), static_cast(Param(system, 2)), + static_cast(Param(system, 3)), static_cast(Param(system, 4))) .raw; FuncReturn(system, retval); } // Used by SignalToAddress32 -template +template void SvcWrap32(Core::System& system) { - const u32 retval = func(system, static_cast(Param(system, 0)), - static_cast(Param(system, 1)), - static_cast(Param(system, 2)), static_cast(Param(system, 3))) - .raw; + const u32 retval = + func(system, static_cast(Param(system, 0)), static_cast(Param(system, 1)), + static_cast(Param(system, 2)), static_cast(Param(system, 3))) + .raw; FuncReturn(system, retval); } @@ -546,9 +539,9 @@ void SvcWrap32(Core::System& system) { } // Used by WaitSynchronization32 -template +template void SvcWrap32(Core::System& system) { - s32 param_1 = 0; + u32 param_1 = 0; const u32 retval = func(system, Param32(system, 0), Param32(system, 1), Param32(system, 2), Param32(system, 3), ¶m_1) .raw; diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index eda56c31c..a4f9e0d97 100755 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -17,11 +17,9 @@ #include "core/hardware_properties.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/handle_table.h" -#include "core/hle/kernel/k_condition_variable.h" #include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" #include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/memory/memory_layout.h" #include "core/hle/kernel/object.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/thread.h" @@ -36,19 +34,26 @@ namespace Kernel { -bool Thread::IsSignaled() const { - return signaled; +bool Thread::ShouldWait(const Thread* thread) const { + return status != ThreadStatus::Dead; } -Thread::Thread(KernelCore& kernel) : KSynchronizationObject{kernel} {} +bool Thread::IsSignaled() const { + return status == ThreadStatus::Dead; +} + +void Thread::Acquire(Thread* thread) { + ASSERT_MSG(!ShouldWait(thread), "object unavailable!"); +} + +Thread::Thread(KernelCore& kernel) : SynchronizationObject{kernel} {} Thread::~Thread() = default; void Thread::Stop() { { KScopedSchedulerLock lock(kernel); - SetState(ThreadState::Terminated); - signaled = true; - NotifyAvailable(); + SetStatus(ThreadStatus::Dead); + Signal(); kernel.GlobalHandleTable().Close(global_handle); if (owner_process) { @@ -62,27 +67,59 @@ void Thread::Stop() { global_handle = 0; } -void Thread::Wakeup() { +void Thread::ResumeFromWait() { KScopedSchedulerLock lock(kernel); - SetState(ThreadState::Runnable); + switch (status) { + case ThreadStatus::Paused: + case ThreadStatus::WaitSynch: + case ThreadStatus::WaitHLEEvent: + case ThreadStatus::WaitSleep: + case ThreadStatus::WaitIPC: + case ThreadStatus::WaitMutex: + case ThreadStatus::WaitCondVar: + case ThreadStatus::WaitArb: + case ThreadStatus::Dormant: + break; + + case ThreadStatus::Ready: + // The thread's wakeup callback must have already been cleared when the thread was first + // awoken. + ASSERT(hle_callback == nullptr); + // If the thread is waiting on multiple wait objects, it might be awoken more than once + // before actually resuming. We can ignore subsequent wakeups if the thread status has + // already been set to ThreadStatus::Ready. + return; + case ThreadStatus::Dead: + // This should never happen, as threads must complete before being stopped. + DEBUG_ASSERT_MSG(false, "Thread with object id {} cannot be resumed because it's DEAD.", + GetObjectId()); + return; + } + + SetStatus(ThreadStatus::Ready); +} + +void Thread::OnWakeUp() { + KScopedSchedulerLock lock(kernel); + SetStatus(ThreadStatus::Ready); } ResultCode Thread::Start() { KScopedSchedulerLock lock(kernel); - SetState(ThreadState::Runnable); + SetStatus(ThreadStatus::Ready); return RESULT_SUCCESS; } void Thread::CancelWait() { KScopedSchedulerLock lock(kernel); - if (GetState() != ThreadState::Waiting || !is_cancellable) { + if (GetSchedulingStatus() != ThreadSchedStatus::Paused || !is_waiting_on_sync) { is_sync_cancelled = true; return; } // TODO(Blinkhawk): Implement cancel of server session is_sync_cancelled = false; SetSynchronizationResults(nullptr, ERR_SYNCHRONIZATION_CANCELED); - SetState(ThreadState::Runnable); + SetStatus(ThreadStatus::Ready); } static void ResetThreadContext32(Core::ARM_Interface::ThreadContext32& context, u32 stack_top, @@ -146,24 +183,25 @@ ResultVal> Thread::Create(Core::System& system, ThreadTy std::shared_ptr thread = std::make_shared(kernel); thread->thread_id = kernel.CreateNewThreadID(); - thread->thread_state = ThreadState::Initialized; + thread->status = ThreadStatus::Dormant; thread->entry_point = entry_point; thread->stack_top = stack_top; thread->disable_count = 1; thread->tpidr_el0 = 0; - thread->current_priority = priority; - thread->base_priority = priority; - thread->lock_owner = nullptr; + thread->nominal_priority = thread->current_priority = priority; thread->schedule_count = -1; thread->last_scheduled_tick = 0; thread->processor_id = processor_id; thread->ideal_core = processor_id; thread->affinity_mask.SetAffinity(processor_id, true); + thread->wait_objects = nullptr; + thread->mutex_wait_address = 0; + thread->condvar_wait_address = 0; + thread->wait_handle = 0; thread->name = std::move(name); thread->global_handle = kernel.GlobalHandleTable().Create(thread).Unwrap(); thread->owner_process = owner_process; thread->type = type_flags; - thread->signaled = false; if ((type_flags & THREADTYPE_IDLE) == 0) { auto& scheduler = kernel.GlobalSchedulerContext(); scheduler.AddThread(thread); @@ -188,182 +226,153 @@ ResultVal> Thread::Create(Core::System& system, ThreadTy return MakeResult>(std::move(thread)); } -void Thread::SetBasePriority(u32 priority) { +void Thread::SetPriority(u32 priority) { + KScopedSchedulerLock lock(kernel); ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST, "Invalid priority value."); - - KScopedSchedulerLock lock(kernel); - - // Change our base priority. - base_priority = priority; - - // Perform a priority restoration. - RestorePriority(kernel, this); + nominal_priority = priority; + UpdatePriority(); } -void Thread::SetSynchronizationResults(KSynchronizationObject* object, ResultCode result) { +void Thread::SetSynchronizationResults(SynchronizationObject* object, ResultCode result) { signaling_object = object; signaling_result = result; } +s32 Thread::GetSynchronizationObjectIndex(std::shared_ptr object) const { + ASSERT_MSG(!wait_objects->empty(), "Thread is not waiting for anything"); + const auto match = std::find(wait_objects->rbegin(), wait_objects->rend(), object); + return static_cast(std::distance(match, wait_objects->rend()) - 1); +} + VAddr Thread::GetCommandBufferAddress() const { // Offset from the start of TLS at which the IPC command buffer begins. constexpr u64 command_header_offset = 0x80; return GetTLSAddress() + command_header_offset; } -void Thread::SetState(ThreadState state) { - KScopedSchedulerLock sl(kernel); - - SetMutexWaitAddressForDebugging(0); - const ThreadState old_state = thread_state; - thread_state = - static_cast((old_state & ~ThreadState::Mask) | (state & ThreadState::Mask)); - if (thread_state != old_state) { - KScheduler::OnThreadStateChanged(kernel, this, old_state); +void Thread::SetStatus(ThreadStatus new_status) { + if (new_status == status) { + return; } + + switch (new_status) { + case ThreadStatus::Ready: + SetSchedulingStatus(ThreadSchedStatus::Runnable); + break; + case ThreadStatus::Dormant: + SetSchedulingStatus(ThreadSchedStatus::None); + break; + case ThreadStatus::Dead: + SetSchedulingStatus(ThreadSchedStatus::Exited); + break; + default: + SetSchedulingStatus(ThreadSchedStatus::Paused); + break; + } + + status = new_status; } -void Thread::AddWaiterImpl(Thread* thread) { - ASSERT(kernel.GlobalSchedulerContext().IsLocked()); - - // Find the right spot to insert the waiter. - auto it = waiter_list.begin(); - while (it != waiter_list.end()) { - if (it->GetPriority() > thread->GetPriority()) { - break; - } - it++; +void Thread::AddMutexWaiter(std::shared_ptr thread) { + if (thread->lock_owner.get() == this) { + // If the thread is already waiting for this thread to release the mutex, ensure that the + // waiters list is consistent and return without doing anything. + const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread); + ASSERT(iter != wait_mutex_threads.end()); + return; } - // Keep track of how many kernel waiters we have. - if (Memory::IsKernelAddressKey(thread->GetAddressKey())) { - ASSERT((num_kernel_waiters++) >= 0); - } + // A thread can't wait on two different mutexes at the same time. + ASSERT(thread->lock_owner == nullptr); - // Insert the waiter. - waiter_list.insert(it, *thread); - thread->SetLockOwner(this); + // Ensure that the thread is not already in the list of mutex waiters + const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread); + ASSERT(iter == wait_mutex_threads.end()); + + // Keep the list in an ordered fashion + const auto insertion_point = std::find_if( + wait_mutex_threads.begin(), wait_mutex_threads.end(), + [&thread](const auto& entry) { return entry->GetPriority() > thread->GetPriority(); }); + wait_mutex_threads.insert(insertion_point, thread); + thread->lock_owner = SharedFrom(this); + + UpdatePriority(); } -void Thread::RemoveWaiterImpl(Thread* thread) { - ASSERT(kernel.GlobalSchedulerContext().IsLocked()); +void Thread::RemoveMutexWaiter(std::shared_ptr thread) { + ASSERT(thread->lock_owner.get() == this); - // Keep track of how many kernel waiters we have. - if (Memory::IsKernelAddressKey(thread->GetAddressKey())) { - ASSERT((num_kernel_waiters--) > 0); - } + // Ensure that the thread is in the list of mutex waiters + const auto iter = std::find(wait_mutex_threads.begin(), wait_mutex_threads.end(), thread); + ASSERT(iter != wait_mutex_threads.end()); - // Remove the waiter. - waiter_list.erase(waiter_list.iterator_to(*thread)); - thread->SetLockOwner(nullptr); + wait_mutex_threads.erase(iter); + + thread->lock_owner = nullptr; + UpdatePriority(); } -void Thread::RestorePriority(KernelCore& kernel, Thread* thread) { - ASSERT(kernel.GlobalSchedulerContext().IsLocked()); - - while (true) { - // We want to inherit priority where possible. - s32 new_priority = thread->GetBasePriority(); - if (thread->HasWaiters()) { - new_priority = std::min(new_priority, thread->waiter_list.front().GetPriority()); - } - - // If the priority we would inherit is not different from ours, don't do anything. - if (new_priority == thread->GetPriority()) { - return; - } - - // Ensure we don't violate condition variable red black tree invariants. - if (auto* cv_tree = thread->GetConditionVariableTree(); cv_tree != nullptr) { - BeforeUpdatePriority(kernel, cv_tree, thread); - } - - // Change the priority. - const s32 old_priority = thread->GetPriority(); - thread->SetPriority(new_priority); - - // Restore the condition variable, if relevant. - if (auto* cv_tree = thread->GetConditionVariableTree(); cv_tree != nullptr) { - AfterUpdatePriority(kernel, cv_tree, thread); - } - - // Update the scheduler. - KScheduler::OnThreadPriorityChanged(kernel, thread, old_priority); - - // Keep the lock owner up to date. - Thread* lock_owner = thread->GetLockOwner(); - if (lock_owner == nullptr) { - return; - } - - // Update the thread in the lock owner's sorted list, and continue inheriting. - lock_owner->RemoveWaiterImpl(thread); - lock_owner->AddWaiterImpl(thread); - thread = lock_owner; - } -} - -void Thread::AddWaiter(Thread* thread) { - AddWaiterImpl(thread); - RestorePriority(kernel, this); -} - -void Thread::RemoveWaiter(Thread* thread) { - RemoveWaiterImpl(thread); - RestorePriority(kernel, this); -} - -Thread* Thread::RemoveWaiterByKey(s32* out_num_waiters, VAddr key) { - ASSERT(kernel.GlobalSchedulerContext().IsLocked()); - - s32 num_waiters{}; - Thread* next_lock_owner{}; - auto it = waiter_list.begin(); - while (it != waiter_list.end()) { - if (it->GetAddressKey() == key) { - Thread* thread = std::addressof(*it); - - // Keep track of how many kernel waiters we have. - if (Memory::IsKernelAddressKey(thread->GetAddressKey())) { - ASSERT((num_kernel_waiters--) > 0); - } - it = waiter_list.erase(it); - - // Update the next lock owner. - if (next_lock_owner == nullptr) { - next_lock_owner = thread; - next_lock_owner->SetLockOwner(nullptr); - } else { - next_lock_owner->AddWaiterImpl(thread); - } - num_waiters++; - } else { - it++; +void Thread::UpdatePriority() { + // If any of the threads waiting on the mutex have a higher priority + // (taking into account priority inheritance), then this thread inherits + // that thread's priority. + u32 new_priority = nominal_priority; + if (!wait_mutex_threads.empty()) { + if (wait_mutex_threads.front()->current_priority < new_priority) { + new_priority = wait_mutex_threads.front()->current_priority; } } - // Do priority updates, if we have a next owner. - if (next_lock_owner) { - RestorePriority(kernel, this); - RestorePriority(kernel, next_lock_owner); + if (new_priority == current_priority) { + return; } - // Return output. - *out_num_waiters = num_waiters; - return next_lock_owner; + if (GetStatus() == ThreadStatus::WaitCondVar) { + owner_process->RemoveConditionVariableThread(SharedFrom(this)); + } + + SetCurrentPriority(new_priority); + + if (GetStatus() == ThreadStatus::WaitCondVar) { + owner_process->InsertConditionVariableThread(SharedFrom(this)); + } + + if (!lock_owner) { + return; + } + + // Ensure that the thread is within the correct location in the waiting list. + auto old_owner = lock_owner; + lock_owner->RemoveMutexWaiter(SharedFrom(this)); + old_owner->AddMutexWaiter(SharedFrom(this)); + + // Recursively update the priority of the thread that depends on the priority of this one. + lock_owner->UpdatePriority(); +} + +bool Thread::AllSynchronizationObjectsReady() const { + return std::none_of(wait_objects->begin(), wait_objects->end(), + [this](const std::shared_ptr& object) { + return object->ShouldWait(this); + }); +} + +bool Thread::InvokeHLECallback(std::shared_ptr thread) { + ASSERT(hle_callback); + return hle_callback(std::move(thread)); } ResultCode Thread::SetActivity(ThreadActivity value) { KScopedSchedulerLock lock(kernel); - auto sched_status = GetState(); + auto sched_status = GetSchedulingStatus(); - if (sched_status != ThreadState::Runnable && sched_status != ThreadState::Waiting) { + if (sched_status != ThreadSchedStatus::Runnable && sched_status != ThreadSchedStatus::Paused) { return ERR_INVALID_STATE; } - if (IsTerminationRequested()) { + if (IsPendingTermination()) { return RESULT_SUCCESS; } @@ -385,7 +394,7 @@ ResultCode Thread::Sleep(s64 nanoseconds) { Handle event_handle{}; { KScopedSchedulerLockAndSleep lock(kernel, event_handle, this, nanoseconds); - SetState(ThreadState::Waiting); + SetStatus(ThreadStatus::WaitSleep); } if (event_handle != InvalidHandle) { @@ -396,21 +405,34 @@ ResultCode Thread::Sleep(s64 nanoseconds) { } void Thread::AddSchedulingFlag(ThreadSchedFlags flag) { - const auto old_state = GetRawState(); + const u32 old_state = scheduling_state; pausing_state |= static_cast(flag); - const auto base_scheduling = GetState(); - thread_state = base_scheduling | static_cast(pausing_state); + const u32 base_scheduling = static_cast(GetSchedulingStatus()); + scheduling_state = base_scheduling | pausing_state; KScheduler::OnThreadStateChanged(kernel, this, old_state); } void Thread::RemoveSchedulingFlag(ThreadSchedFlags flag) { - const auto old_state = GetRawState(); + const u32 old_state = scheduling_state; pausing_state &= ~static_cast(flag); - const auto base_scheduling = GetState(); - thread_state = base_scheduling | static_cast(pausing_state); + const u32 base_scheduling = static_cast(GetSchedulingStatus()); + scheduling_state = base_scheduling | pausing_state; KScheduler::OnThreadStateChanged(kernel, this, old_state); } +void Thread::SetSchedulingStatus(ThreadSchedStatus new_status) { + const u32 old_state = scheduling_state; + scheduling_state = (scheduling_state & static_cast(ThreadSchedMasks::HighMask)) | + static_cast(new_status); + KScheduler::OnThreadStateChanged(kernel, this, old_state); +} + +void Thread::SetCurrentPriority(u32 new_priority) { + const u32 old_priority = std::exchange(current_priority, new_priority); + KScheduler::OnThreadPriorityChanged(kernel, this, kernel.CurrentScheduler()->GetCurrentThread(), + old_priority); +} + ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) { KScopedSchedulerLock lock(kernel); const auto HighestSetCore = [](u64 mask, u32 max_cores) { diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index bf6bca2e4..11ef29888 100755 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -10,16 +10,12 @@ #include #include -#include - #include "common/common_types.h" -#include "common/intrusive_red_black_tree.h" #include "common/spin_lock.h" #include "core/arm/arm_interface.h" #include "core/hle/kernel/k_affinity_mask.h" -#include "core/hle/kernel/k_synchronization_object.h" #include "core/hle/kernel/object.h" -#include "core/hle/kernel/svc_common.h" +#include "core/hle/kernel/synchronization_object.h" #include "core/hle/result.h" namespace Common { @@ -77,24 +73,19 @@ enum ThreadProcessorId : s32 { (1 << THREADPROCESSORID_2) | (1 << THREADPROCESSORID_3) }; -enum class ThreadState : u16 { - Initialized = 0, - Waiting = 1, - Runnable = 2, - Terminated = 3, - - SuspendShift = 4, - Mask = (1 << SuspendShift) - 1, - - ProcessSuspended = (1 << (0 + SuspendShift)), - ThreadSuspended = (1 << (1 + SuspendShift)), - DebugSuspended = (1 << (2 + SuspendShift)), - BacktraceSuspended = (1 << (3 + SuspendShift)), - InitSuspended = (1 << (4 + SuspendShift)), - - SuspendFlagMask = ((1 << 5) - 1) << SuspendShift, +enum class ThreadStatus { + Ready, ///< Ready to run + Paused, ///< Paused by SetThreadActivity or debug + WaitHLEEvent, ///< Waiting for hle event to finish + WaitSleep, ///< Waiting due to a SleepThread SVC + WaitIPC, ///< Waiting for the reply from an IPC request + WaitSynch, ///< Waiting due to WaitSynchronization + WaitMutex, ///< Waiting due to an ArbitrateLock svc + WaitCondVar, ///< Waiting due to an WaitProcessWideKey svc + WaitArb, ///< Waiting due to a SignalToAddress/WaitForAddress svc + Dormant, ///< Created but not yet made ready + Dead ///< Run to completion, or forcefully terminated }; -DECLARE_ENUM_FLAG_OPERATORS(ThreadState); enum class ThreadWakeupReason { Signal, // The thread was woken up by WakeupAllWaitingThreads due to an object signal. @@ -106,6 +97,13 @@ enum class ThreadActivity : u32 { Paused = 1, }; +enum class ThreadSchedStatus : u32 { + None = 0, + Paused = 1, + Runnable = 2, + Exited = 3, +}; + enum class ThreadSchedFlags : u32 { ProcessPauseFlag = 1 << 4, ThreadPauseFlag = 1 << 5, @@ -113,10 +111,13 @@ enum class ThreadSchedFlags : u32 { KernelInitPauseFlag = 1 << 8, }; -class Thread final : public KSynchronizationObject, public boost::intrusive::list_base_hook<> { - friend class KScheduler; - friend class Process; +enum class ThreadSchedMasks : u32 { + LowMask = 0x000f, + HighMask = 0xfff0, + ForcePauseMask = 0x0070, +}; +class Thread final : public SynchronizationObject { public: explicit Thread(KernelCore& kernel); ~Thread() override; @@ -126,6 +127,10 @@ public: using ThreadContext32 = Core::ARM_Interface::ThreadContext32; using ThreadContext64 = Core::ARM_Interface::ThreadContext64; + using ThreadSynchronizationObjects = std::vector>; + + using HLECallback = std::function thread)>; + /** * Creates and returns a new thread. The new thread is immediately scheduled * @param system The instance of the whole system @@ -181,54 +186,59 @@ public: return HANDLE_TYPE; } + bool ShouldWait(const Thread* thread) const override; + void Acquire(Thread* thread) override; + bool IsSignaled() const override; + /** * Gets the thread's current priority * @return The current thread's priority */ - [[nodiscard]] s32 GetPriority() const { + u32 GetPriority() const { return current_priority; } - /** - * Sets the thread's current priority. - * @param priority The new priority. - */ - void SetPriority(s32 priority) { - current_priority = priority; - } - /** * Gets the thread's nominal priority. * @return The current thread's nominal priority. */ - [[nodiscard]] s32 GetBasePriority() const { - return base_priority; + u32 GetNominalPriority() const { + return nominal_priority; } /** - * Sets the thread's nominal priority. - * @param priority The new priority. + * Sets the thread's current priority + * @param priority The new priority */ - void SetBasePriority(u32 priority); + void SetPriority(u32 priority); + + /// Adds a thread to the list of threads that are waiting for a lock held by this thread. + void AddMutexWaiter(std::shared_ptr thread); + + /// Removes a thread from the list of threads that are waiting for a lock held by this thread. + void RemoveMutexWaiter(std::shared_ptr thread); + + /// Recalculates the current priority taking into account priority inheritance. + void UpdatePriority(); /// Changes the core that the thread is running or scheduled to run on. - [[nodiscard]] ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask); + ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask); /** * Gets the thread's thread ID * @return The thread's ID */ - [[nodiscard]] u64 GetThreadID() const { + u64 GetThreadID() const { return thread_id; } /// Resumes a thread from waiting - void Wakeup(); + void ResumeFromWait(); + + void OnWakeUp(); ResultCode Start(); - virtual bool IsSignaled() const override; - /// Cancels a waiting operation that this thread may or may not be within. /// /// When the thread is within a waiting state, this will set the thread's @@ -237,21 +247,30 @@ public: /// void CancelWait(); - void SetSynchronizationResults(KSynchronizationObject* object, ResultCode result); + void SetSynchronizationResults(SynchronizationObject* object, ResultCode result); - void SetSyncedObject(KSynchronizationObject* object, ResultCode result) { - SetSynchronizationResults(object, result); - } - - ResultCode GetWaitResult(KSynchronizationObject** out) const { - *out = signaling_object; - return signaling_result; + SynchronizationObject* GetSignalingObject() const { + return signaling_object; } ResultCode GetSignalingResult() const { return signaling_result; } + /** + * Retrieves the index that this particular object occupies in the list of objects + * that the thread passed to WaitSynchronization, starting the search from the last element. + * + * It is used to set the output index of WaitSynchronization when the thread is awakened. + * + * When a thread wakes up due to an object signal, the kernel will use the index of the last + * matching object in the wait objects list in case of having multiple instances of the same + * object in the list. + * + * @param object Object to query the index of. + */ + s32 GetSynchronizationObjectIndex(std::shared_ptr object) const; + /** * Stops a thread, invalidating it from further use */ @@ -322,22 +341,18 @@ public: std::shared_ptr& GetHostContext(); - ThreadState GetState() const { - return thread_state & ThreadState::Mask; + ThreadStatus GetStatus() const { + return status; } - ThreadState GetRawState() const { - return thread_state; - } - - void SetState(ThreadState state); + void SetStatus(ThreadStatus new_status); s64 GetLastScheduledTick() const { - return last_scheduled_tick; + return this->last_scheduled_tick; } void SetLastScheduledTick(s64 tick) { - last_scheduled_tick = tick; + this->last_scheduled_tick = tick; } u64 GetTotalCPUTimeTicks() const { @@ -372,18 +387,98 @@ public: return owner_process; } + const ThreadSynchronizationObjects& GetSynchronizationObjects() const { + return *wait_objects; + } + + void SetSynchronizationObjects(ThreadSynchronizationObjects* objects) { + wait_objects = objects; + } + + void ClearSynchronizationObjects() { + for (const auto& waiting_object : *wait_objects) { + waiting_object->RemoveWaitingThread(SharedFrom(this)); + } + wait_objects->clear(); + } + + /// Determines whether all the objects this thread is waiting on are ready. + bool AllSynchronizationObjectsReady() const; + const MutexWaitingThreads& GetMutexWaitingThreads() const { return wait_mutex_threads; } Thread* GetLockOwner() const { - return lock_owner; + return lock_owner.get(); } - void SetLockOwner(Thread* owner) { - lock_owner = owner; + void SetLockOwner(std::shared_ptr owner) { + lock_owner = std::move(owner); } + VAddr GetCondVarWaitAddress() const { + return condvar_wait_address; + } + + void SetCondVarWaitAddress(VAddr address) { + condvar_wait_address = address; + } + + VAddr GetMutexWaitAddress() const { + return mutex_wait_address; + } + + void SetMutexWaitAddress(VAddr address) { + mutex_wait_address = address; + } + + Handle GetWaitHandle() const { + return wait_handle; + } + + void SetWaitHandle(Handle handle) { + wait_handle = handle; + } + + VAddr GetArbiterWaitAddress() const { + return arb_wait_address; + } + + void SetArbiterWaitAddress(VAddr address) { + arb_wait_address = address; + } + + bool HasHLECallback() const { + return hle_callback != nullptr; + } + + void SetHLECallback(HLECallback callback) { + hle_callback = std::move(callback); + } + + void SetHLETimeEvent(Handle time_event) { + hle_time_event = time_event; + } + + void SetHLESyncObject(SynchronizationObject* object) { + hle_object = object; + } + + Handle GetHLETimeEvent() const { + return hle_time_event; + } + + SynchronizationObject* GetHLESyncObject() const { + return hle_object; + } + + void InvalidateHLECallback() { + SetHLECallback(nullptr); + } + + bool InvokeHLECallback(std::shared_ptr thread); + u32 GetIdealCore() const { return ideal_core; } @@ -398,11 +493,20 @@ public: ResultCode Sleep(s64 nanoseconds); s64 GetYieldScheduleCount() const { - return schedule_count; + return this->schedule_count; } void SetYieldScheduleCount(s64 count) { - schedule_count = count; + this->schedule_count = count; + } + + ThreadSchedStatus GetSchedulingStatus() const { + return static_cast(scheduling_state & + static_cast(ThreadSchedMasks::LowMask)); + } + + bool IsRunnable() const { + return scheduling_state == static_cast(ThreadSchedStatus::Runnable); } bool IsRunning() const { @@ -413,32 +517,36 @@ public: is_running = value; } - bool IsWaitCancelled() const { + bool IsSyncCancelled() const { return is_sync_cancelled; } - void ClearWaitCancelled() { - is_sync_cancelled = false; + void SetSyncCancelled(bool value) { + is_sync_cancelled = value; } Handle GetGlobalHandle() const { return global_handle; } - bool IsCancellable() const { - return is_cancellable; + bool IsWaitingForArbitration() const { + return waiting_for_arbitration; } - void SetCancellable() { - is_cancellable = true; + void WaitForArbitration(bool set) { + waiting_for_arbitration = set; } - void ClearCancellable() { - is_cancellable = false; + bool IsWaitingSync() const { + return is_waiting_on_sync; } - bool IsTerminationRequested() const { - return will_be_terminated || GetRawState() == ThreadState::Terminated; + void SetWaitingSync(bool is_waiting) { + is_waiting_on_sync = is_waiting; + } + + bool IsPendingTermination() const { + return will_be_terminated || GetSchedulingStatus() == ThreadSchedStatus::Exited; } bool IsPaused() const { @@ -470,21 +578,21 @@ public: constexpr QueueEntry() = default; constexpr void Initialize() { - prev = nullptr; - next = nullptr; + this->prev = nullptr; + this->next = nullptr; } constexpr Thread* GetPrev() const { - return prev; + return this->prev; } constexpr Thread* GetNext() const { - return next; + return this->next; } constexpr void SetPrev(Thread* thread) { - prev = thread; + this->prev = thread; } constexpr void SetNext(Thread* thread) { - next = thread; + this->next = thread; } private: @@ -493,11 +601,11 @@ public: }; QueueEntry& GetPriorityQueueEntry(s32 core) { - return per_core_priority_queue_entry[core]; + return this->per_core_priority_queue_entry[core]; } const QueueEntry& GetPriorityQueueEntry(s32 core) const { - return per_core_priority_queue_entry[core]; + return this->per_core_priority_queue_entry[core]; } s32 GetDisableDispatchCount() const { @@ -514,162 +622,24 @@ public: disable_count--; } - void SetWaitObjectsForDebugging(KSynchronizationObject** objects, s32 num_objects) { - wait_objects_for_debugging.clear(); - wait_objects_for_debugging.reserve(num_objects); - for (auto i = 0; i < num_objects; ++i) { - wait_objects_for_debugging.emplace_back(objects[i]); - } - } - - [[nodiscard]] const std::vector& GetWaitObjectsForDebugging() const { - return wait_objects_for_debugging; - } - - void SetMutexWaitAddressForDebugging(VAddr address) { - mutex_wait_address_for_debugging = address; - } - - [[nodiscard]] VAddr GetMutexWaitAddressForDebugging() const { - return mutex_wait_address_for_debugging; - } - - void AddWaiter(Thread* thread); - - void RemoveWaiter(Thread* thread); - - [[nodiscard]] Thread* RemoveWaiterByKey(s32* out_num_waiters, VAddr key); - - [[nodiscard]] VAddr GetAddressKey() const { - return address_key; - } - - [[nodiscard]] u32 GetAddressKeyValue() const { - return address_key_value; - } - - [[nodiscard]] void SetAddressKey(VAddr key) { - address_key = key; - } - - [[nodiscard]] void SetAddressKey(VAddr key, u32 val) { - address_key = key; - address_key_value = val; - } - private: - static constexpr size_t PriorityInheritanceCountMax = 10; - union SyncObjectBuffer { - std::array sync_objects; - std::array - handles; - constexpr SyncObjectBuffer() : sync_objects() {} - }; - static_assert(sizeof(SyncObjectBuffer::sync_objects) == sizeof(SyncObjectBuffer::handles)); + friend class GlobalSchedulerContext; + friend class KScheduler; + friend class Process; - struct ConditionVariableComparator { - struct LightCompareType { - u64 cv_key{}; - s32 priority{}; - - [[nodiscard]] constexpr u64 GetConditionVariableKey() const { - return cv_key; - } - - [[nodiscard]] constexpr s32 GetPriority() const { - return priority; - } - }; - - template - requires( - std::same_as || - std::same_as) static constexpr int Compare(const T& lhs, - const Thread& rhs) { - const uintptr_t l_key = lhs.GetConditionVariableKey(); - const uintptr_t r_key = rhs.GetConditionVariableKey(); - - if (l_key < r_key) { - // Sort first by key - return -1; - } else if (l_key == r_key && lhs.GetPriority() < rhs.GetPriority()) { - // And then by priority. - return -1; - } else { - return 1; - } - } - }; - - Common::IntrusiveRedBlackTreeNode condvar_arbiter_tree_node{}; - - using ConditionVariableThreadTreeTraits = - Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<&Thread::condvar_arbiter_tree_node>; - using ConditionVariableThreadTree = - ConditionVariableThreadTreeTraits::TreeType; - -public: - using ConditionVariableThreadTreeType = ConditionVariableThreadTree; - - [[nodiscard]] uintptr_t GetConditionVariableKey() const { - return condvar_key; - } - - [[nodiscard]] uintptr_t GetAddressArbiterKey() const { - return condvar_key; - } - - void SetConditionVariable(ConditionVariableThreadTree* tree, VAddr address, uintptr_t cv_key, - u32 value) { - condvar_tree = tree; - condvar_key = cv_key; - address_key = address; - address_key_value = value; - } - - void ClearConditionVariable() { - condvar_tree = nullptr; - } - - [[nodiscard]] bool IsWaitingForConditionVariable() const { - return condvar_tree != nullptr; - } - - void SetAddressArbiter(ConditionVariableThreadTree* tree, uintptr_t address) { - condvar_tree = tree; - condvar_key = address; - } - - void ClearAddressArbiter() { - condvar_tree = nullptr; - } - - [[nodiscard]] bool IsWaitingForAddressArbiter() const { - return condvar_tree != nullptr; - } - - [[nodiscard]] ConditionVariableThreadTree* GetConditionVariableTree() const { - return condvar_tree; - } - - [[nodiscard]] bool HasWaiters() const { - return !waiter_list.empty(); - } - -private: + void SetSchedulingStatus(ThreadSchedStatus new_status); void AddSchedulingFlag(ThreadSchedFlags flag); void RemoveSchedulingFlag(ThreadSchedFlags flag); - void AddWaiterImpl(Thread* thread); - void RemoveWaiterImpl(Thread* thread); - static void RestorePriority(KernelCore& kernel, Thread* thread); + + void SetCurrentPriority(u32 new_priority); Common::SpinLock context_guard{}; ThreadContext32 context_32{}; ThreadContext64 context_64{}; std::shared_ptr host_context{}; - ThreadState thread_state = ThreadState::Initialized; + ThreadStatus status = ThreadStatus::Dormant; + u32 scheduling_state = 0; u64 thread_id = 0; @@ -682,11 +652,11 @@ private: /// Nominal thread priority, as set by the emulated application. /// The nominal priority is the thread priority without priority /// inheritance taken into account. - s32 base_priority{}; + u32 nominal_priority = 0; /// Current thread priority. This may change over the course of the /// thread's lifetime in order to facilitate priority inheritance. - s32 current_priority{}; + u32 current_priority = 0; u64 total_cpu_time_ticks = 0; ///< Total CPU running ticks. s64 schedule_count{}; @@ -701,24 +671,37 @@ private: Process* owner_process; /// Objects that the thread is waiting on, in the same order as they were - /// passed to WaitSynchronization. This is used for debugging only. - std::vector wait_objects_for_debugging; + /// passed to WaitSynchronization. + ThreadSynchronizationObjects* wait_objects; - /// The current mutex wait address. This is used for debugging only. - VAddr mutex_wait_address_for_debugging{}; - - KSynchronizationObject* signaling_object; + SynchronizationObject* signaling_object; ResultCode signaling_result{RESULT_SUCCESS}; /// List of threads that are waiting for a mutex that is held by this thread. MutexWaitingThreads wait_mutex_threads; /// Thread that owns the lock that this thread is waiting for. - Thread* lock_owner{}; + std::shared_ptr lock_owner; + + /// If waiting on a ConditionVariable, this is the ConditionVariable address + VAddr condvar_wait_address = 0; + /// If waiting on a Mutex, this is the mutex address + VAddr mutex_wait_address = 0; + /// The handle used to wait for the mutex. + Handle wait_handle = 0; + + /// If waiting for an AddressArbiter, this is the address being waited on. + VAddr arb_wait_address{0}; + bool waiting_for_arbitration{}; /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. Handle global_handle = 0; + /// Callback for HLE Events + HLECallback hle_callback; + Handle hle_time_event; + SynchronizationObject* hle_object; + KScheduler* scheduler = nullptr; std::array per_core_priority_queue_entry{}; @@ -731,7 +714,7 @@ private: u32 pausing_state = 0; bool is_running = false; - bool is_cancellable = false; + bool is_waiting_on_sync = false; bool is_sync_cancelled = false; bool is_continuous_on_svc = false; @@ -742,18 +725,6 @@ private: bool was_running = false; - bool signaled{}; - - ConditionVariableThreadTree* condvar_tree{}; - uintptr_t condvar_key{}; - VAddr address_key{}; - u32 address_key_value{}; - s32 num_kernel_waiters{}; - - using WaiterList = boost::intrusive::list; - WaiterList waiter_list{}; - WaiterList pinned_waiter_list{}; - std::string name; }; diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp index 832edd629..79628e2b4 100755 --- a/src/core/hle/kernel/time_manager.cpp +++ b/src/core/hle/kernel/time_manager.cpp @@ -18,10 +18,12 @@ TimeManager::TimeManager(Core::System& system_) : system{system_} { time_manager_event_type = Core::Timing::CreateEvent( "Kernel::TimeManagerCallback", [this](std::uintptr_t thread_handle, std::chrono::nanoseconds) { + const KScopedSchedulerLock lock(system.Kernel()); + const auto proper_handle = static_cast(thread_handle); + std::shared_ptr thread; { std::lock_guard lock{mutex}; - const auto proper_handle = static_cast(thread_handle); if (cancelled_events[proper_handle]) { return; } @@ -30,7 +32,7 @@ TimeManager::TimeManager(Core::System& system_) : system{system_} { if (thread) { // Thread can be null if process has exited - thread->Wakeup(); + thread->OnWakeUp(); } }); } @@ -40,7 +42,8 @@ void TimeManager::ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 event_handle = timetask->GetGlobalHandle(); if (nanoseconds > 0) { ASSERT(timetask); - ASSERT(timetask->GetState() != ThreadState::Runnable); + ASSERT(timetask->GetStatus() != ThreadStatus::Ready); + ASSERT(timetask->GetStatus() != ThreadStatus::WaitMutex); system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{nanoseconds}, time_manager_event_type, event_handle); } else { diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index 641bcadea..5557da72e 100755 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp @@ -190,6 +190,12 @@ private: void GetDeviceState(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NFP, "called"); + auto nfc_event = nfp_interface.GetNFCEvent(); + if (!nfc_event->ShouldWait(&ctx.GetThread()) && !has_attached_handle) { + device_state = DeviceState::TagFound; + nfc_event->Clear(); + } + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); rb.Push(static_cast(device_state)); diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index ceaa93d28..4b3581949 100755 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -38,10 +38,6 @@ void NVFlinger::SplitVSync() { system.RegisterHostThread(); std::string name = "yuzu:VSyncThread"; MicroProfileOnThreadCreate(name.c_str()); - - // Cleanup - SCOPE_EXIT({ MicroProfileOnThreadExit(); }); - Common::SetCurrentThreadName(name.c_str()); Common::SetCurrentThreadPriority(Common::ThreadPriority::High); s64 delay = 0; diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index 2b91a89d1..4da69f503 100755 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -139,6 +139,9 @@ void SM::GetService(Kernel::HLERequestContext& ctx) { server_port->AppendPendingSession(server); } + // Wake the threads waiting on the ServerPort + server_port->Signal(); + LOG_DEBUG(Service_SM, "called service={} -> session={}", name, client->GetObjectId()); IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; rb.Push(RESULT_SUCCESS); diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp index d32eb732a..0b531f698 100755 --- a/src/input_common/sdl/sdl_impl.cpp +++ b/src/input_common/sdl/sdl_impl.cpp @@ -1030,44 +1030,11 @@ public: } return {}; } - [[nodiscard]] std::optional FromEvent(SDL_Event& event) { + [[nodiscard]] std::optional FromEvent(const SDL_Event& event) const { switch (event.type) { case SDL_JOYAXISMOTION: - if (!axis_memory.count(event.jaxis.which) || - !axis_memory[event.jaxis.which].count(event.jaxis.axis)) { - axis_memory[event.jaxis.which][event.jaxis.axis] = event.jaxis.value; - axis_event_count[event.jaxis.which][event.jaxis.axis] = 1; + if (std::abs(event.jaxis.value / 32767.0) < 0.5) { break; - } else { - axis_event_count[event.jaxis.which][event.jaxis.axis]++; - // The joystick and axis exist in our map if we take this branch, so no checks - // needed - if (std::abs( - (event.jaxis.value - axis_memory[event.jaxis.which][event.jaxis.axis]) / - 32767.0) < 0.5) { - break; - } else { - if (axis_event_count[event.jaxis.which][event.jaxis.axis] == 2 && - IsAxisAtPole(event.jaxis.value) && - IsAxisAtPole(axis_memory[event.jaxis.which][event.jaxis.axis])) { - // If we have exactly two events and both are near a pole, this is - // likely a digital input masquerading as an analog axis; Instead of - // trying to look at the direction the axis travelled, assume the first - // event was press and the second was release; This should handle most - // digital axes while deferring to the direction of travel for analog - // axes - event.jaxis.value = static_cast( - std::copysign(32767, axis_memory[event.jaxis.which][event.jaxis.axis])); - } else { - // There are more than two events, so this is likely a true analog axis, - // check the direction it travelled - event.jaxis.value = static_cast(std::copysign( - 32767, - event.jaxis.value - axis_memory[event.jaxis.which][event.jaxis.axis])); - } - axis_memory.clear(); - axis_event_count.clear(); - } } [[fallthrough]]; case SDL_JOYBUTTONUP: @@ -1076,16 +1043,6 @@ public: } return std::nullopt; } - -private: - // Determine whether an axis value is close to an extreme or center - // Some controllers have a digital D-Pad as a pair of analog sticks, with 3 possible values per - // axis, which is why the center must be considered a pole - bool IsAxisAtPole(int16_t value) const { - return std::abs(value) >= 32767 || std::abs(value) < 327; - } - std::unordered_map> axis_memory; - std::unordered_map> axis_event_count; }; class SDLMotionPoller final : public SDLPoller { diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index 3ded85720..0925c10b4 100755 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp @@ -14,10 +14,10 @@ #include "core/core.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/k_scheduler.h" -#include "core/hle/kernel/k_synchronization_object.h" +#include "core/hle/kernel/mutex.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/readable_event.h" -#include "core/hle/kernel/svc_common.h" +#include "core/hle/kernel/synchronization_object.h" #include "core/hle/kernel/thread.h" #include "core/memory.h" @@ -116,7 +116,7 @@ QString WaitTreeText::GetText() const { WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address, const Kernel::HandleTable& handle_table) : mutex_address(mutex_address) { mutex_value = Core::System::GetInstance().Memory().Read32(mutex_address); - owner_handle = static_cast(mutex_value & Kernel::Svc::HandleWaitMask); + owner_handle = static_cast(mutex_value & Kernel::Mutex::MutexOwnerMask); owner = handle_table.Get(owner_handle); } @@ -127,7 +127,7 @@ QString WaitTreeMutexInfo::GetText() const { } std::vector> WaitTreeMutexInfo::GetChildren() const { - const bool has_waiters = (mutex_value & Kernel::Svc::HandleWaitMask) != 0; + const bool has_waiters = (mutex_value & Kernel::Mutex::MutexHasWaitersFlag) != 0; std::vector> list; list.push_back(std::make_unique(tr("has waiters: %1").arg(has_waiters))); @@ -169,8 +169,7 @@ std::vector> WaitTreeCallstack::GetChildren() cons return list; } -WaitTreeSynchronizationObject::WaitTreeSynchronizationObject( - const Kernel::KSynchronizationObject& o) +WaitTreeSynchronizationObject::WaitTreeSynchronizationObject(const Kernel::SynchronizationObject& o) : object(o) {} WaitTreeSynchronizationObject::~WaitTreeSynchronizationObject() = default; @@ -189,7 +188,7 @@ QString WaitTreeSynchronizationObject::GetText() const { } std::unique_ptr WaitTreeSynchronizationObject::make( - const Kernel::KSynchronizationObject& object) { + const Kernel::SynchronizationObject& object) { switch (object.GetHandleType()) { case Kernel::HandleType::ReadableEvent: return std::make_unique(static_cast(object)); @@ -203,7 +202,7 @@ std::unique_ptr WaitTreeSynchronizationObject::ma std::vector> WaitTreeSynchronizationObject::GetChildren() const { std::vector> list; - const auto& threads = object.GetWaitingThreadsForDebugging(); + const auto& threads = object.GetWaitingThreads(); if (threads.empty()) { list.push_back(std::make_unique(tr("waited by no thread"))); } else { @@ -212,8 +211,8 @@ std::vector> WaitTreeSynchronizationObject::GetChi return list; } -WaitTreeObjectList::WaitTreeObjectList(const std::vector& list, - bool w_all) +WaitTreeObjectList::WaitTreeObjectList( + const std::vector>& list, bool w_all) : object_list(list), wait_all(w_all) {} WaitTreeObjectList::~WaitTreeObjectList() = default; @@ -238,8 +237,8 @@ WaitTreeThread::~WaitTreeThread() = default; QString WaitTreeThread::GetText() const { const auto& thread = static_cast(object); QString status; - switch (thread.GetState()) { - case Kernel::ThreadState::Runnable: + switch (thread.GetStatus()) { + case Kernel::ThreadStatus::Ready: if (!thread.IsPaused()) { if (thread.WasRunning()) { status = tr("running"); @@ -250,14 +249,35 @@ QString WaitTreeThread::GetText() const { status = tr("paused"); } break; - case Kernel::ThreadState::Waiting: - status = tr("waiting"); + case Kernel::ThreadStatus::Paused: + status = tr("paused"); break; - case Kernel::ThreadState::Initialized: - status = tr("initialized"); + case Kernel::ThreadStatus::WaitHLEEvent: + status = tr("waiting for HLE return"); break; - case Kernel::ThreadState::Terminated: - status = tr("terminated"); + case Kernel::ThreadStatus::WaitSleep: + status = tr("sleeping"); + break; + case Kernel::ThreadStatus::WaitIPC: + status = tr("waiting for IPC reply"); + break; + case Kernel::ThreadStatus::WaitSynch: + status = tr("waiting for objects"); + break; + case Kernel::ThreadStatus::WaitMutex: + status = tr("waiting for mutex"); + break; + case Kernel::ThreadStatus::WaitCondVar: + status = tr("waiting for condition variable"); + break; + case Kernel::ThreadStatus::WaitArb: + status = tr("waiting for address arbiter"); + break; + case Kernel::ThreadStatus::Dormant: + status = tr("dormant"); + break; + case Kernel::ThreadStatus::Dead: + status = tr("dead"); break; } @@ -273,8 +293,8 @@ QColor WaitTreeThread::GetColor() const { const std::size_t color_index = IsDarkTheme() ? 1 : 0; const auto& thread = static_cast(object); - switch (thread.GetState()) { - case Kernel::ThreadState::Runnable: + switch (thread.GetStatus()) { + case Kernel::ThreadStatus::Ready: if (!thread.IsPaused()) { if (thread.WasRunning()) { return QColor(WaitTreeColors[0][color_index]); @@ -284,11 +304,21 @@ QColor WaitTreeThread::GetColor() const { } else { return QColor(WaitTreeColors[2][color_index]); } - case Kernel::ThreadState::Waiting: + case Kernel::ThreadStatus::Paused: return QColor(WaitTreeColors[3][color_index]); - case Kernel::ThreadState::Initialized: + case Kernel::ThreadStatus::WaitHLEEvent: + case Kernel::ThreadStatus::WaitIPC: + return QColor(WaitTreeColors[4][color_index]); + case Kernel::ThreadStatus::WaitSleep: + return QColor(WaitTreeColors[5][color_index]); + case Kernel::ThreadStatus::WaitSynch: + case Kernel::ThreadStatus::WaitMutex: + case Kernel::ThreadStatus::WaitCondVar: + case Kernel::ThreadStatus::WaitArb: + return QColor(WaitTreeColors[6][color_index]); + case Kernel::ThreadStatus::Dormant: return QColor(WaitTreeColors[7][color_index]); - case Kernel::ThreadState::Terminated: + case Kernel::ThreadStatus::Dead: return QColor(WaitTreeColors[8][color_index]); default: return WaitTreeItem::GetColor(); @@ -324,11 +354,11 @@ std::vector> WaitTreeThread::GetChildren() const { list.push_back(std::make_unique(tr("thread id = %1").arg(thread.GetThreadID()))); list.push_back(std::make_unique(tr("priority = %1(current) / %2(normal)") .arg(thread.GetPriority()) - .arg(thread.GetBasePriority()))); + .arg(thread.GetNominalPriority()))); list.push_back(std::make_unique( tr("last running ticks = %1").arg(thread.GetLastScheduledTick()))); - const VAddr mutex_wait_address = thread.GetMutexWaitAddressForDebugging(); + const VAddr mutex_wait_address = thread.GetMutexWaitAddress(); if (mutex_wait_address != 0) { const auto& handle_table = thread.GetOwnerProcess()->GetHandleTable(); list.push_back(std::make_unique(mutex_wait_address, handle_table)); @@ -336,9 +366,9 @@ std::vector> WaitTreeThread::GetChildren() const { list.push_back(std::make_unique(tr("not waiting for mutex"))); } - if (thread.GetState() == Kernel::ThreadState::Waiting) { - list.push_back(std::make_unique(thread.GetWaitObjectsForDebugging(), - thread.IsCancellable())); + if (thread.GetStatus() == Kernel::ThreadStatus::WaitSynch) { + list.push_back(std::make_unique(thread.GetSynchronizationObjects(), + thread.IsWaitingSync())); } list.push_back(std::make_unique(thread)); @@ -350,7 +380,7 @@ WaitTreeEvent::WaitTreeEvent(const Kernel::ReadableEvent& object) : WaitTreeSynchronizationObject(object) {} WaitTreeEvent::~WaitTreeEvent() = default; -WaitTreeThreadList::WaitTreeThreadList(const std::vector& list) +WaitTreeThreadList::WaitTreeThreadList(const std::vector>& list) : thread_list(list) {} WaitTreeThreadList::~WaitTreeThreadList() = default; diff --git a/src/yuzu/debugger/wait_tree.h b/src/yuzu/debugger/wait_tree.h index cf96911ea..8e3bc4b24 100755 --- a/src/yuzu/debugger/wait_tree.h +++ b/src/yuzu/debugger/wait_tree.h @@ -18,8 +18,8 @@ class EmuThread; namespace Kernel { class HandleTable; -class KSynchronizationObject; class ReadableEvent; +class SynchronizationObject; class Thread; } // namespace Kernel @@ -102,29 +102,30 @@ private: class WaitTreeSynchronizationObject : public WaitTreeExpandableItem { Q_OBJECT public: - explicit WaitTreeSynchronizationObject(const Kernel::KSynchronizationObject& object); + explicit WaitTreeSynchronizationObject(const Kernel::SynchronizationObject& object); ~WaitTreeSynchronizationObject() override; static std::unique_ptr make( - const Kernel::KSynchronizationObject& object); + const Kernel::SynchronizationObject& object); QString GetText() const override; std::vector> GetChildren() const override; protected: - const Kernel::KSynchronizationObject& object; + const Kernel::SynchronizationObject& object; }; class WaitTreeObjectList : public WaitTreeExpandableItem { Q_OBJECT public: - WaitTreeObjectList(const std::vector& list, bool wait_all); + WaitTreeObjectList(const std::vector>& list, + bool wait_all); ~WaitTreeObjectList() override; QString GetText() const override; std::vector> GetChildren() const override; private: - const std::vector& object_list; + const std::vector>& object_list; bool wait_all; }; @@ -149,14 +150,14 @@ public: class WaitTreeThreadList : public WaitTreeExpandableItem { Q_OBJECT public: - explicit WaitTreeThreadList(const std::vector& list); + explicit WaitTreeThreadList(const std::vector>& list); ~WaitTreeThreadList() override; QString GetText() const override; std::vector> GetChildren() const override; private: - const std::vector& thread_list; + const std::vector>& thread_list; }; class WaitTreeModel : public QAbstractItemModel {