diff options
89 files changed, 3444 insertions, 2047 deletions
diff --git a/externals/dynarmic b/externals/dynarmic -Subproject 3806284cbefc4115436dcdc687776a45ec31309 +Subproject 8c09da666aa3f0bb1000b0b6c5d5b0a1876f306 diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp index afe68c9ed..5b0b285cd 100644 --- a/src/audio_core/stream.cpp +++ b/src/audio_core/stream.cpp @@ -51,6 +51,14 @@ void Stream::Stop() { UNIMPLEMENTED(); } +bool Stream::Flush() { + const bool had_buffers = !queued_buffers.empty(); + while (!queued_buffers.empty()) { + queued_buffers.pop(); + } + return had_buffers; +} + void Stream::SetVolume(float volume) { game_volume = volume; } diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h index 506ac536b..559844b9b 100644 --- a/src/audio_core/stream.h +++ b/src/audio_core/stream.h @@ -56,6 +56,9 @@ public: /// Queues a buffer into the audio stream, returns true on success bool QueueBuffer(BufferPtr&& buffer); + /// Flush audio buffers + bool Flush(); + /// Returns true if the audio stream contains a buffer with the specified tag [[nodiscard]] bool ContainsBuffer(Buffer::Tag tag) const; diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index 75f3027fb..71b64e32a 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h @@ -97,10 +97,27 @@ __declspec(dllimport) void __stdcall DebugBreak(void); #define R_UNLESS(expr, res) \ { \ if (!(expr)) { \ + if (res.IsError()) { \ + LOG_ERROR(Kernel, "Failed with result: {}", res.raw); \ + } \ return res; \ } \ } +#define R_SUCCEEDED(res) (res.IsSuccess()) + +/// Evaluates an expression that returns a result, and returns the result if it would fail. +#define R_TRY(res_expr) \ + { \ + const auto _tmp_r_try_rc = (res_expr); \ + if (_tmp_r_try_rc.IsError()) { \ + return _tmp_r_try_rc; \ + } \ + } + +/// Evaluates a boolean expression, and succeeds if that expression is true. +#define R_SUCCEED_IF(expr) R_UNLESS(!(expr), RESULT_SUCCESS) + 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 9c9b3195b..397cc028f 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -160,6 +160,8 @@ add_library(core STATIC hle/kernel/k_affinity_mask.h hle/kernel/k_condition_variable.cpp hle/kernel/k_condition_variable.h + hle/kernel/k_light_lock.cpp + hle/kernel/k_light_lock.h hle/kernel/k_priority_queue.h hle/kernel/k_scheduler.cpp hle/kernel/k_scheduler.h @@ -168,6 +170,9 @@ add_library(core STATIC hle/kernel/k_scoped_scheduler_lock_and_sleep.h hle/kernel/k_synchronization_object.cpp hle/kernel/k_synchronization_object.h + hle/kernel/k_thread.cpp + hle/kernel/k_thread.h + hle/kernel/k_thread_queue.h hle/kernel/kernel.cpp hle/kernel/kernel.h hle/kernel/memory/address_space_info.cpp @@ -216,8 +221,6 @@ add_library(core STATIC hle/kernel/svc_results.h hle/kernel/svc_types.h hle/kernel/svc_wrap.h - hle/kernel/thread.cpp - hle/kernel/thread.h hle/kernel/time_manager.cpp hle/kernel/time_manager.h hle/kernel/transfer_memory.cpp diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 6c4c8e9e4..ec4407b6e 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -71,8 +71,9 @@ public: } void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override { - LOG_CRITICAL(Core_ARM, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", - exception, pc, MemoryReadCode(pc)); + LOG_CRITICAL(Core_ARM, + "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X}, thumb = {})", + exception, pc, MemoryReadCode(pc), parent.IsInThumbMode()); UNIMPLEMENTED(); } @@ -255,6 +256,9 @@ void ARM_Dynarmic_32::ChangeProcessorID(std::size_t new_core_id) { } void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) { + if (!jit) { + return; + } Dynarmic::A32::Context context; jit->SaveContext(context); ctx.cpu_registers = context.Regs(); @@ -264,6 +268,9 @@ void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) { } void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) { + if (!jit) { + return; + } Dynarmic::A32::Context context; context.Regs() = ctx.cpu_registers; context.ExtRegs() = ctx.extension_registers; diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h index 35e9ced48..f6c4d4db9 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.h +++ b/src/core/arm/dynarmic/arm_dynarmic_32.h @@ -50,6 +50,10 @@ public: u64 GetTPIDR_EL0() const override; void ChangeProcessorID(std::size_t new_core_id) override; + bool IsInThumbMode() const { + return (GetPSTATE() & 0x20) != 0; + } + void SaveContext(ThreadContext32& ctx) override; void SaveContext(ThreadContext64& ctx) override {} void LoadContext(const ThreadContext32& ctx) override; diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index 4c5ebca22..ae5566ab8 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -294,6 +294,9 @@ void ARM_Dynarmic_64::ChangeProcessorID(std::size_t new_core_id) { } void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) { + if (!jit) { + return; + } ctx.cpu_registers = jit->GetRegisters(); ctx.sp = jit->GetSP(); ctx.pc = jit->GetPC(); @@ -305,6 +308,9 @@ void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) { } void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) { + if (!jit) { + return; + } jit->SetRegisters(ctx.cpu_registers); jit->SetSP(ctx.sp); jit->SetPC(ctx.pc); diff --git a/src/core/core.cpp b/src/core/core.cpp index 86bdc7f6b..30f5e1128 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -28,10 +28,10 @@ #include "core/hardware_interrupt_manager.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/k_scheduler.h" +#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/physical_core.h" #include "core/hle/kernel/process.h" -#include "core/hle/kernel/thread.h" #include "core/hle/service/am/applets/applets.h" #include "core/hle/service/apm/controller.h" #include "core/hle/service/filesystem/filesystem.h" diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp index 373395047..8f04fb8f5 100644 --- a/src/core/cpu_manager.cpp +++ b/src/core/cpu_manager.cpp @@ -11,9 +11,9 @@ #include "core/core_timing.h" #include "core/cpu_manager.h" #include "core/hle/kernel/k_scheduler.h" +#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/physical_core.h" -#include "core/hle/kernel/thread.h" #include "video_core/gpu.h" namespace Core { @@ -147,7 +147,7 @@ void CpuManager::MultiCoreRunSuspendThread() { while (true) { auto core = kernel.GetCurrentHostThreadID(); auto& scheduler = *kernel.CurrentScheduler(); - Kernel::Thread* current_thread = scheduler.GetCurrentThread(); + Kernel::KThread* current_thread = scheduler.GetCurrentThread(); Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[core].host_context); ASSERT(scheduler.ContextSwitchPending()); ASSERT(core == kernel.GetCurrentHostThreadID()); @@ -208,7 +208,6 @@ void CpuManager::SingleCoreRunGuestThread() { void CpuManager::SingleCoreRunGuestLoop() { auto& kernel = system.Kernel(); - auto* thread = kernel.CurrentScheduler()->GetCurrentThread(); while (true) { auto* physical_core = &kernel.CurrentPhysicalCore(); system.EnterDynarmicProfile(); @@ -217,9 +216,9 @@ void CpuManager::SingleCoreRunGuestLoop() { physical_core = &kernel.CurrentPhysicalCore(); } system.ExitDynarmicProfile(); - thread->SetPhantomMode(true); + kernel.SetIsPhantomModeForSingleCore(true); system.CoreTiming().Advance(); - thread->SetPhantomMode(false); + kernel.SetIsPhantomModeForSingleCore(false); physical_core->ArmInterface().ClearExclusiveState(); PreemptSingleCore(); auto& scheduler = kernel.Scheduler(current_core); @@ -245,7 +244,7 @@ void CpuManager::SingleCoreRunSuspendThread() { while (true) { auto core = kernel.GetCurrentHostThreadID(); auto& scheduler = *kernel.CurrentScheduler(); - Kernel::Thread* current_thread = scheduler.GetCurrentThread(); + Kernel::KThread* current_thread = scheduler.GetCurrentThread(); Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[0].host_context); ASSERT(scheduler.ContextSwitchPending()); ASSERT(core == kernel.GetCurrentHostThreadID()); @@ -255,22 +254,23 @@ void CpuManager::SingleCoreRunSuspendThread() { void CpuManager::PreemptSingleCore(bool from_running_enviroment) { { - auto& scheduler = system.Kernel().Scheduler(current_core); - Kernel::Thread* current_thread = scheduler.GetCurrentThread(); + auto& kernel = system.Kernel(); + auto& scheduler = kernel.Scheduler(current_core); + Kernel::KThread* current_thread = scheduler.GetCurrentThread(); if (idle_count >= 4 || from_running_enviroment) { if (!from_running_enviroment) { system.CoreTiming().Idle(); idle_count = 0; } - current_thread->SetPhantomMode(true); + kernel.SetIsPhantomModeForSingleCore(true); system.CoreTiming().Advance(); - current_thread->SetPhantomMode(false); + kernel.SetIsPhantomModeForSingleCore(false); } current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES); system.CoreTiming().ResetTicks(); scheduler.Unload(scheduler.GetCurrentThread()); - auto& next_scheduler = system.Kernel().Scheduler(current_core); + auto& next_scheduler = kernel.Scheduler(current_core); Common::Fiber::YieldTo(current_thread->GetHostContext(), next_scheduler.ControlContext()); } @@ -278,8 +278,7 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) { { auto& scheduler = system.Kernel().Scheduler(current_core); scheduler.Reload(scheduler.GetCurrentThread()); - auto* currrent_thread2 = scheduler.GetCurrentThread(); - if (!currrent_thread2->IsIdleThread()) { + if (!scheduler.IsIdle()) { idle_count = 0; } } diff --git a/src/core/hardware_properties.h b/src/core/hardware_properties.h index 456b41e1b..176a72c67 100644 --- a/src/core/hardware_properties.h +++ b/src/core/hardware_properties.h @@ -4,8 +4,10 @@ #pragma once +#include <array> #include <tuple> +#include "common/bit_util.h" #include "common/common_types.h" namespace Core { @@ -18,34 +20,12 @@ constexpr u64 BASE_CLOCK_RATE = 1019215872; // Switch cpu frequency is 1020MHz u constexpr u64 CNTFREQ = 19200000; // Switch's hardware clock speed constexpr u32 NUM_CPU_CORES = 4; // Number of CPU Cores -} // namespace Hardware - -constexpr u32 INVALID_HOST_THREAD_ID = 0xFFFFFFFF; - -struct EmuThreadHandle { - u32 host_handle; - u32 guest_handle; - - u64 GetRaw() const { - return (static_cast<u64>(host_handle) << 32) | guest_handle; - } - - bool operator==(const EmuThreadHandle& rhs) const { - return std::tie(host_handle, guest_handle) == std::tie(rhs.host_handle, rhs.guest_handle); - } - - bool operator!=(const EmuThreadHandle& rhs) const { - return !operator==(rhs); - } - - static constexpr EmuThreadHandle InvalidHandle() { - constexpr u32 invalid_handle = 0xFFFFFFFF; - return {invalid_handle, invalid_handle}; - } - - bool IsInvalid() const { - return (*this) == InvalidHandle(); - } +// Virtual to Physical core map. +constexpr std::array<s32, Common::BitSize<u64>()> VirtualToPhysicalCoreMap{ + 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, }; +} // namespace Hardware + } // namespace Core diff --git a/src/core/hle/kernel/client_port.h b/src/core/hle/kernel/client_port.h index 9762bbf0d..77559ebf9 100644 --- a/src/core/hle/kernel/client_port.h +++ b/src/core/hle/kernel/client_port.h @@ -51,6 +51,8 @@ public: */ void ConnectionClosed(); + void Finalize() override {} + private: std::shared_ptr<ServerPort> server_port; ///< ServerPort associated with this client port. u32 max_sessions = 0; ///< Maximum number of simultaneous sessions the port can have diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp index e8e52900d..a2be1a8f6 100644 --- a/src/core/hle/kernel/client_session.cpp +++ b/src/core/hle/kernel/client_session.cpp @@ -5,9 +5,9 @@ #include "core/hle/kernel/client_session.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/hle_ipc.h" +#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/server_session.h" #include "core/hle/kernel/session.h" -#include "core/hle/kernel/thread.h" #include "core/hle/result.h" namespace Kernel { @@ -38,7 +38,7 @@ ResultVal<std::shared_ptr<ClientSession>> ClientSession::Create(KernelCore& kern return MakeResult(std::move(client_session)); } -ResultCode ClientSession::SendSyncRequest(std::shared_ptr<Thread> thread, +ResultCode ClientSession::SendSyncRequest(std::shared_ptr<KThread> thread, Core::Memory::Memory& memory, Core::Timing::CoreTiming& core_timing) { // Keep ServerSession alive until we're done working with it. diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h index d5c9ebee8..85aafeaf4 100644 --- a/src/core/hle/kernel/client_session.h +++ b/src/core/hle/kernel/client_session.h @@ -24,7 +24,7 @@ namespace Kernel { class KernelCore; class Session; -class Thread; +class KThread; class ClientSession final : public KSynchronizationObject { public: @@ -46,11 +46,13 @@ public: return HANDLE_TYPE; } - ResultCode SendSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory, + ResultCode SendSyncRequest(std::shared_ptr<KThread> thread, Core::Memory::Memory& memory, Core::Timing::CoreTiming& core_timing); bool IsSignaled() const override; + void Finalize() override {} + private: static ResultVal<std::shared_ptr<ClientSession>> Create(KernelCore& kernel, std::shared_ptr<Session> parent, diff --git a/src/core/hle/kernel/global_scheduler_context.cpp b/src/core/hle/kernel/global_scheduler_context.cpp index a133e8ed0..c6838649f 100644 --- a/src/core/hle/kernel/global_scheduler_context.cpp +++ b/src/core/hle/kernel/global_scheduler_context.cpp @@ -17,12 +17,12 @@ GlobalSchedulerContext::GlobalSchedulerContext(KernelCore& kernel) GlobalSchedulerContext::~GlobalSchedulerContext() = default; -void GlobalSchedulerContext::AddThread(std::shared_ptr<Thread> thread) { +void GlobalSchedulerContext::AddThread(std::shared_ptr<KThread> thread) { std::scoped_lock lock{global_list_guard}; thread_list.push_back(std::move(thread)); } -void GlobalSchedulerContext::RemoveThread(std::shared_ptr<Thread> thread) { +void GlobalSchedulerContext::RemoveThread(std::shared_ptr<KThread> thread) { std::scoped_lock lock{global_list_guard}; thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread), thread_list.end()); diff --git a/src/core/hle/kernel/global_scheduler_context.h b/src/core/hle/kernel/global_scheduler_context.h index 5c7b89290..11592843e 100644 --- a/src/core/hle/kernel/global_scheduler_context.h +++ b/src/core/hle/kernel/global_scheduler_context.h @@ -12,7 +12,8 @@ #include "core/hardware_properties.h" #include "core/hle/kernel/k_priority_queue.h" #include "core/hle/kernel/k_scheduler_lock.h" -#include "core/hle/kernel/thread.h" +#include "core/hle/kernel/k_thread.h" +#include "core/hle/kernel/svc_types.h" namespace Kernel { @@ -20,8 +21,12 @@ class KernelCore; class SchedulerLock; using KSchedulerPriorityQueue = - KPriorityQueue<Thread, Core::Hardware::NUM_CPU_CORES, THREADPRIO_LOWEST, THREADPRIO_HIGHEST>; -constexpr s32 HighestCoreMigrationAllowedPriority = 2; + KPriorityQueue<KThread, Core::Hardware::NUM_CPU_CORES, Svc::LowestThreadPriority, + Svc::HighestThreadPriority>; + +static constexpr s32 HighestCoreMigrationAllowedPriority = 2; +static_assert(Svc::LowestThreadPriority >= HighestCoreMigrationAllowedPriority); +static_assert(Svc::HighestThreadPriority <= HighestCoreMigrationAllowedPriority); class GlobalSchedulerContext final { friend class KScheduler; @@ -33,13 +38,13 @@ public: ~GlobalSchedulerContext(); /// Adds a new thread to the scheduler - void AddThread(std::shared_ptr<Thread> thread); + void AddThread(std::shared_ptr<KThread> thread); /// Removes a thread from the scheduler - void RemoveThread(std::shared_ptr<Thread> thread); + void RemoveThread(std::shared_ptr<KThread> thread); /// Returns a list of all threads managed by the scheduler - [[nodiscard]] const std::vector<std::shared_ptr<Thread>>& GetThreadList() const { + [[nodiscard]] const std::vector<std::shared_ptr<KThread>>& GetThreadList() const { return thread_list; } @@ -74,7 +79,7 @@ private: LockType scheduler_lock; /// Lists all thread ids that aren't deleted/etc. - std::vector<std::shared_ptr<Thread>> thread_list; + std::vector<std::shared_ptr<KThread>> thread_list; Common::SpinLock global_list_guard{}; }; diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp index 40988b0fd..1a2fa9cd8 100644 --- a/src/core/hle/kernel/handle_table.cpp +++ b/src/core/hle/kernel/handle_table.cpp @@ -9,9 +9,9 @@ #include "core/hle/kernel/errors.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/k_scheduler.h" +#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" -#include "core/hle/kernel/thread.h" namespace Kernel { namespace { @@ -89,6 +89,10 @@ ResultCode HandleTable::Close(Handle handle) { const u16 slot = GetSlot(handle); + if (objects[slot].use_count() == 1) { + objects[slot]->Finalize(); + } + objects[slot] = nullptr; generations[slot] = next_free_slot; diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 83decf6cf..c7b10ca7a 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -19,12 +19,12 @@ #include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" +#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/object.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/server_session.h" -#include "core/hle/kernel/thread.h" #include "core/hle/kernel/time_manager.h" #include "core/hle/kernel/writable_event.h" #include "core/memory.h" @@ -48,7 +48,7 @@ void SessionRequestHandler::ClientDisconnected( HLERequestContext::HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory, std::shared_ptr<ServerSession> server_session, - std::shared_ptr<Thread> thread) + std::shared_ptr<KThread> thread) : server_session(std::move(server_session)), thread(std::move(thread)), kernel{kernel}, memory{memory} { cmd_buf[0] = 0; @@ -182,7 +182,7 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const HandleTabl return RESULT_SUCCESS; } -ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) { +ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(KThread& thread) { auto& owner_process = *thread.GetOwnerProcess(); auto& handle_table = owner_process.GetHandleTable(); @@ -338,6 +338,28 @@ std::size_t HLERequestContext::GetWriteBufferSize(std::size_t buffer_index) cons return 0; } +bool HLERequestContext::CanReadBuffer(std::size_t buffer_index) const { + const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && + BufferDescriptorA()[buffer_index].Size()}; + + if (is_buffer_a) { + return BufferDescriptorA().size() > buffer_index; + } else { + return BufferDescriptorX().size() > buffer_index; + } +} + +bool HLERequestContext::CanWriteBuffer(std::size_t buffer_index) const { + const bool is_buffer_b{BufferDescriptorB().size() > buffer_index && + BufferDescriptorB()[buffer_index].Size()}; + + if (is_buffer_b) { + return BufferDescriptorB().size() > buffer_index; + } else { + return BufferDescriptorC().size() > buffer_index; + } +} + std::string HLERequestContext::Description() const { if (!command_header) { return "No command header available"; diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index b112e1ebd..9f764c79a 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -40,7 +40,7 @@ class HLERequestContext; class KernelCore; class Process; class ServerSession; -class Thread; +class KThread; class ReadableEvent; class WritableEvent; @@ -110,7 +110,7 @@ class HLERequestContext { public: explicit HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory, std::shared_ptr<ServerSession> session, - std::shared_ptr<Thread> thread); + std::shared_ptr<KThread> thread); ~HLERequestContext(); /// Returns a pointer to the IPC command buffer for this request. @@ -126,15 +126,12 @@ public: return server_session; } - using WakeupCallback = std::function<void( - std::shared_ptr<Thread> thread, HLERequestContext& context, ThreadWakeupReason reason)>; - /// Populates this context with data from the requesting process/thread. ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf); /// Writes data from this context back to the requesting process/thread. - ResultCode WriteToOutgoingCommandBuffer(Thread& thread); + ResultCode WriteToOutgoingCommandBuffer(KThread& thread); u32_le GetCommand() const { return command; @@ -207,6 +204,12 @@ public: /// Helper function to get the size of the output buffer std::size_t GetWriteBufferSize(std::size_t buffer_index = 0) const; + /// Helper function to test whether the input buffer at buffer_index can be read + bool CanReadBuffer(std::size_t buffer_index = 0) const; + + /// Helper function to test whether the output buffer at buffer_index can be written + bool CanWriteBuffer(std::size_t buffer_index = 0) const; + template <typename T> std::shared_ptr<T> GetCopyObject(std::size_t index) { return DynamicObjectCast<T>(copy_objects.at(index)); @@ -261,11 +264,11 @@ public: std::string Description() const; - Thread& GetThread() { + KThread& GetThread() { return *thread; } - const Thread& GetThread() const { + const KThread& GetThread() const { return *thread; } @@ -280,7 +283,7 @@ private: std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf; std::shared_ptr<Kernel::ServerSession> server_session; - std::shared_ptr<Thread> thread; + std::shared_ptr<KThread> thread; // TODO(yuriks): Check common usage of this and optimize size accordingly boost::container::small_vector<std::shared_ptr<Object>, 8> move_objects; boost::container::small_vector<std::shared_ptr<Object>, 8> copy_objects; diff --git a/src/core/hle/kernel/k_address_arbiter.cpp b/src/core/hle/kernel/k_address_arbiter.cpp index d9e702f13..1685d25bb 100644 --- a/src/core/hle/kernel/k_address_arbiter.cpp +++ b/src/core/hle/kernel/k_address_arbiter.cpp @@ -7,9 +7,9 @@ #include "core/hle/kernel/k_address_arbiter.h" #include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" +#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/svc_results.h" -#include "core/hle/kernel/thread.h" #include "core/hle/kernel/time_manager.h" #include "core/memory.h" @@ -96,7 +96,7 @@ ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) { auto it = thread_tree.nfind_light({addr, -1}); while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && (it->GetAddressArbiterKey() == addr)) { - Thread* target_thread = std::addressof(*it); + KThread* target_thread = std::addressof(*it); target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS); ASSERT(target_thread->IsWaitingForAddressArbiter()); @@ -125,7 +125,7 @@ ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32 auto it = thread_tree.nfind_light({addr, -1}); while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && (it->GetAddressArbiterKey() == addr)) { - Thread* target_thread = std::addressof(*it); + KThread* target_thread = std::addressof(*it); target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS); ASSERT(target_thread->IsWaitingForAddressArbiter()); @@ -215,7 +215,7 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && (it->GetAddressArbiterKey() == addr)) { - Thread* target_thread = std::addressof(*it); + KThread* target_thread = std::addressof(*it); target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS); ASSERT(target_thread->IsWaitingForAddressArbiter()); @@ -231,11 +231,10 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32 ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) { // Prepare to wait. - Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); - Handle timer = InvalidHandle; + KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); { - KScopedSchedulerLockAndSleep slp(kernel, timer, cur_thread, timeout); + KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout}; // Check that the thread isn't terminating. if (cur_thread->IsTerminationRequested()) { @@ -280,10 +279,7 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement } // Cancel the timer wait. - if (timer != InvalidHandle) { - auto& time_manager = kernel.TimeManager(); - time_manager.UnscheduleTimeEvent(timer); - } + kernel.TimeManager().UnscheduleTimeEvent(cur_thread); // Remove from the address arbiter. { @@ -302,11 +298,10 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) { // Prepare to wait. - Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); - Handle timer = InvalidHandle; + KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); { - KScopedSchedulerLockAndSleep slp(kernel, timer, cur_thread, timeout); + KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout}; // Check that the thread isn't terminating. if (cur_thread->IsTerminationRequested()) { @@ -344,10 +339,7 @@ ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) { } // Cancel the timer wait. - if (timer != InvalidHandle) { - auto& time_manager = kernel.TimeManager(); - time_manager.UnscheduleTimeEvent(timer); - } + kernel.TimeManager().UnscheduleTimeEvent(cur_thread); // Remove from the address arbiter. { diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp index 49a068310..f0ad8b390 100644 --- a/src/core/hle/kernel/k_condition_variable.cpp +++ b/src/core/hle/kernel/k_condition_variable.cpp @@ -10,11 +10,11 @@ #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/k_thread.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/svc_common.h" #include "core/hle/kernel/svc_results.h" -#include "core/hle/kernel/thread.h" #include "core/memory.h" namespace Kernel { @@ -66,7 +66,7 @@ KConditionVariable::KConditionVariable(Core::System& system_) KConditionVariable::~KConditionVariable() = default; ResultCode KConditionVariable::SignalToAddress(VAddr addr) { - Thread* owner_thread = kernel.CurrentScheduler()->GetCurrentThread(); + KThread* owner_thread = kernel.CurrentScheduler()->GetCurrentThread(); // Signal the address. { @@ -74,7 +74,7 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) { // Remove waiter thread. s32 num_waiters{}; - Thread* next_owner_thread = + KThread* next_owner_thread = owner_thread->RemoveWaiterByKey(std::addressof(num_waiters), addr); // Determine the next tag. @@ -103,11 +103,11 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) { } ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) { - Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); + KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); // Wait for the address. { - std::shared_ptr<Thread> owner_thread; + std::shared_ptr<KThread> owner_thread; ASSERT(!owner_thread); { KScopedSchedulerLock sl(kernel); @@ -126,7 +126,7 @@ ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 val R_UNLESS(test_tag == (handle | Svc::HandleWaitMask), RESULT_SUCCESS); // Get the lock owner thread. - owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<Thread>(handle); + owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<KThread>(handle); R_UNLESS(owner_thread, Svc::ResultInvalidHandle); // Update the lock. @@ -143,7 +143,7 @@ ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 val // Remove the thread as a waiter from the lock owner. { KScopedSchedulerLock sl(kernel); - Thread* owner_thread = cur_thread->GetLockOwner(); + KThread* owner_thread = cur_thread->GetLockOwner(); if (owner_thread != nullptr) { owner_thread->RemoveWaiter(cur_thread); } @@ -154,7 +154,7 @@ ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 val return cur_thread->GetWaitResult(std::addressof(dummy)); } -Thread* KConditionVariable::SignalImpl(Thread* thread) { +KThread* KConditionVariable::SignalImpl(KThread* thread) { // Check pre-conditions. ASSERT(kernel.GlobalSchedulerContext().IsLocked()); @@ -174,7 +174,7 @@ Thread* KConditionVariable::SignalImpl(Thread* thread) { } } - Thread* thread_to_close = nullptr; + KThread* thread_to_close = nullptr; if (can_access) { if (prev_tag == InvalidHandle) { // If nobody held the lock previously, we're all good. @@ -182,7 +182,7 @@ Thread* KConditionVariable::SignalImpl(Thread* thread) { thread->Wakeup(); } else { // Get the previous owner. - auto owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<Thread>( + auto owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<KThread>( prev_tag & ~Svc::HandleWaitMask); if (owner_thread) { @@ -210,8 +210,8 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) { // TODO(bunnei): This should just be Thread once we implement KAutoObject instead of using // std::shared_ptr. - std::vector<std::shared_ptr<Thread>> thread_list; - std::array<Thread*, MaxThreads> thread_array; + std::vector<std::shared_ptr<KThread>> thread_list; + std::array<KThread*, MaxThreads> thread_array; s32 num_to_close{}; // Perform signaling. @@ -222,9 +222,9 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) { auto it = thread_tree.nfind_light({cv_key, -1}); while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && (it->GetConditionVariableKey() == cv_key)) { - Thread* target_thread = std::addressof(*it); + KThread* target_thread = std::addressof(*it); - if (Thread* thread = SignalImpl(target_thread); thread != nullptr) { + if (KThread* thread = SignalImpl(target_thread); thread != nullptr) { if (num_to_close < MaxThreads) { thread_array[num_to_close++] = thread; } else { @@ -257,11 +257,10 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) { ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) { // Prepare to wait. - Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); - Handle timer = InvalidHandle; + KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); { - KScopedSchedulerLockAndSleep slp(kernel, timer, cur_thread, timeout); + KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout}; // Set the synced object. cur_thread->SetSyncedObject(nullptr, Svc::ResultTimedOut); @@ -276,7 +275,7 @@ ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) { // Remove waiter thread. s32 num_waiters{}; - Thread* next_owner_thread = + KThread* next_owner_thread = cur_thread->RemoveWaiterByKey(std::addressof(num_waiters), addr); // Update for the next owner thread. @@ -322,16 +321,13 @@ ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) } // Cancel the timer wait. - if (timer != InvalidHandle) { - auto& time_manager = kernel.TimeManager(); - time_manager.UnscheduleTimeEvent(timer); - } + kernel.TimeManager().UnscheduleTimeEvent(cur_thread); // Remove from the condition variable. { KScopedSchedulerLock sl(kernel); - if (Thread* owner = cur_thread->GetLockOwner(); owner != nullptr) { + if (KThread* owner = cur_thread->GetLockOwner(); owner != nullptr) { owner->RemoveWaiter(cur_thread); } diff --git a/src/core/hle/kernel/k_condition_variable.h b/src/core/hle/kernel/k_condition_variable.h index 98ed5b323..861dbd420 100644 --- a/src/core/hle/kernel/k_condition_variable.h +++ b/src/core/hle/kernel/k_condition_variable.h @@ -8,8 +8,8 @@ #include "common/common_types.h" #include "core/hle/kernel/k_scheduler.h" +#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/thread.h" #include "core/hle/result.h" namespace Core { @@ -20,7 +20,7 @@ namespace Kernel { class KConditionVariable { public: - using ThreadTree = typename Thread::ConditionVariableThreadTreeType; + using ThreadTree = typename KThread::ConditionVariableThreadTreeType; explicit KConditionVariable(Core::System& system_); ~KConditionVariable(); @@ -34,7 +34,7 @@ public: [[nodiscard]] ResultCode Wait(VAddr addr, u64 key, u32 value, s64 timeout); private: - [[nodiscard]] Thread* SignalImpl(Thread* thread); + [[nodiscard]] KThread* SignalImpl(KThread* thread); ThreadTree thread_tree; @@ -43,14 +43,14 @@ private: }; inline void BeforeUpdatePriority(const KernelCore& kernel, KConditionVariable::ThreadTree* tree, - Thread* thread) { + KThread* thread) { ASSERT(kernel.GlobalSchedulerContext().IsLocked()); tree->erase(tree->iterator_to(*thread)); } inline void AfterUpdatePriority(const KernelCore& kernel, KConditionVariable::ThreadTree* tree, - Thread* thread) { + KThread* thread) { ASSERT(kernel.GlobalSchedulerContext().IsLocked()); tree->insert(*thread); diff --git a/src/core/hle/kernel/k_light_lock.cpp b/src/core/hle/kernel/k_light_lock.cpp new file mode 100644 index 000000000..f974022e8 --- /dev/null +++ b/src/core/hle/kernel/k_light_lock.cpp @@ -0,0 +1,130 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/kernel/k_light_lock.h" +#include "core/hle/kernel/k_scheduler.h" +#include "core/hle/kernel/k_thread.h" +#include "core/hle/kernel/kernel.h" + +namespace Kernel { + +void KLightLock::Lock() { + const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel)); + const uintptr_t cur_thread_tag = (cur_thread | 1); + + while (true) { + uintptr_t old_tag = tag.load(std::memory_order_relaxed); + + while (!tag.compare_exchange_weak(old_tag, (old_tag == 0) ? cur_thread : old_tag | 1, + std::memory_order_acquire)) { + if ((old_tag | 1) == cur_thread_tag) { + return; + } + } + + if ((old_tag == 0) || ((old_tag | 1) == cur_thread_tag)) { + break; + } + + LockSlowPath(old_tag | 1, cur_thread); + } +} + +void KLightLock::Unlock() { + const uintptr_t cur_thread = reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel)); + uintptr_t expected = cur_thread; + do { + if (expected != cur_thread) { + return UnlockSlowPath(cur_thread); + } + } while (!tag.compare_exchange_weak(expected, 0, std::memory_order_release)); +} + +void KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) { + KThread* cur_thread = reinterpret_cast<KThread*>(_cur_thread); + + // Pend the current thread waiting on the owner thread. + { + KScopedSchedulerLock sl{kernel}; + + // Ensure we actually have locking to do. + if (tag.load(std::memory_order_relaxed) != _owner) { + return; + } + + // Add the current thread as a waiter on the owner. + KThread* owner_thread = reinterpret_cast<KThread*>(_owner & ~1ULL); + cur_thread->SetAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag))); + owner_thread->AddWaiter(cur_thread); + + // Set thread states. + if (cur_thread->GetState() == ThreadState::Runnable) { + cur_thread->SetState(ThreadState::Waiting); + } else { + KScheduler::SetSchedulerUpdateNeeded(kernel); + } + + if (owner_thread->IsSuspended()) { + owner_thread->ContinueIfHasKernelWaiters(); + } + } + + // We're no longer waiting on the lock owner. + { + KScopedSchedulerLock sl{kernel}; + KThread* owner_thread = cur_thread->GetLockOwner(); + if (owner_thread) { + owner_thread->RemoveWaiter(cur_thread); + KScheduler::SetSchedulerUpdateNeeded(kernel); + } + } +} + +void KLightLock::UnlockSlowPath(uintptr_t _cur_thread) { + KThread* owner_thread = reinterpret_cast<KThread*>(_cur_thread); + + // Unlock. + { + KScopedSchedulerLock sl{kernel}; + + // Get the next owner. + s32 num_waiters = 0; + KThread* next_owner = owner_thread->RemoveWaiterByKey( + std::addressof(num_waiters), reinterpret_cast<uintptr_t>(std::addressof(tag))); + + // Pass the lock to the next owner. + uintptr_t next_tag = 0; + if (next_owner) { + next_tag = reinterpret_cast<uintptr_t>(next_owner); + if (num_waiters > 1) { + next_tag |= 0x1; + } + + if (next_owner->GetState() == ThreadState::Waiting) { + next_owner->SetState(ThreadState::Runnable); + } else { + KScheduler::SetSchedulerUpdateNeeded(kernel); + } + + if (next_owner->IsSuspended()) { + next_owner->ContinueIfHasKernelWaiters(); + } + } + + // We may have unsuspended in the process of acquiring the lock, so we'll re-suspend now if + // so. + if (owner_thread->IsSuspended()) { + owner_thread->TrySuspend(); + } + + // Write the new tag value. + tag.store(next_tag); + } +} + +bool KLightLock::IsLockedByCurrentThread() const { + return (tag | 1ULL) == (reinterpret_cast<uintptr_t>(GetCurrentThreadPointer(kernel)) | 1ULL); +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_light_lock.h b/src/core/hle/kernel/k_light_lock.h new file mode 100644 index 000000000..f4c45f76a --- /dev/null +++ b/src/core/hle/kernel/k_light_lock.h @@ -0,0 +1,41 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <atomic> + +#include "common/common_types.h" +#include "core/hle/kernel/k_scoped_lock.h" + +namespace Kernel { + +class KernelCore; + +class KLightLock { +public: + explicit KLightLock(KernelCore& kernel_) : kernel{kernel_} {} + + void Lock(); + + void Unlock(); + + void LockSlowPath(uintptr_t owner, uintptr_t cur_thread); + + void UnlockSlowPath(uintptr_t cur_thread); + + bool IsLocked() const { + return tag != 0; + } + + bool IsLockedByCurrentThread() const; + +private: + std::atomic<uintptr_t> tag{}; + KernelCore& kernel; +}; + +using KScopedLightLock = KScopedLock<KLightLock>; + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_priority_queue.h b/src/core/hle/kernel/k_priority_queue.h index 0dc929040..13d628b85 100644 --- a/src/core/hle/kernel/k_priority_queue.h +++ b/src/core/hle/kernel/k_priority_queue.h @@ -18,7 +18,7 @@ namespace Kernel { -class Thread; +class KThread; template <typename T> concept KPriorityQueueAffinityMask = !std::is_reference_v<T> && requires(T & t) { @@ -367,7 +367,7 @@ public: this->scheduled_queue.MoveToFront(member->GetPriority(), member->GetActiveCore(), member); } - constexpr Thread* MoveToScheduledBack(Member* member) { + constexpr KThread* MoveToScheduledBack(Member* member) { return this->scheduled_queue.MoveToBack(member->GetPriority(), member->GetActiveCore(), member); } diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index 12b5619fb..bb5f43b53 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp @@ -17,25 +17,30 @@ #include "core/cpu_manager.h" #include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" +#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/physical_core.h" #include "core/hle/kernel/process.h" -#include "core/hle/kernel/thread.h" #include "core/hle/kernel/time_manager.h" namespace Kernel { -static void IncrementScheduledCount(Kernel::Thread* thread) { +static void IncrementScheduledCount(Kernel::KThread* thread) { if (auto process = thread->GetOwnerProcess(); process) { process->IncrementScheduledCount(); } } -void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule, - Core::EmuThreadHandle global_thread) { - const u32 current_core = global_thread.host_handle; - bool must_context_switch = global_thread.guest_handle != InvalidHandle && - (current_core < Core::Hardware::NUM_CPU_CORES); +void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule) { + auto scheduler = kernel.CurrentScheduler(); + + u32 current_core{0xF}; + bool must_context_switch{}; + if (scheduler) { + current_core = scheduler->core_id; + // TODO(bunnei): Should be set to true when we deprecate single core + must_context_switch = !kernel.IsPhantomModeForSingleCore(); + } while (cores_pending_reschedule != 0) { const auto core = static_cast<u32>(std::countr_zero(cores_pending_reschedule)); @@ -56,28 +61,27 @@ void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedul } } -u64 KScheduler::UpdateHighestPriorityThread(Thread* highest_thread) { +u64 KScheduler::UpdateHighestPriorityThread(KThread* highest_thread) { std::scoped_lock lock{guard}; - if (Thread* prev_highest_thread = this->state.highest_priority_thread; + if (KThread* prev_highest_thread = state.highest_priority_thread; prev_highest_thread != highest_thread) { if (prev_highest_thread != nullptr) { IncrementScheduledCount(prev_highest_thread); prev_highest_thread->SetLastScheduledTick(system.CoreTiming().GetCPUTicks()); } - if (this->state.should_count_idle) { + if (state.should_count_idle) { if (highest_thread != nullptr) { - // if (Process* process = highest_thread->GetOwnerProcess(); process != nullptr) { - // process->SetRunningThread(this->core_id, highest_thread, - // this->state.idle_count); - //} + if (Process* process = highest_thread->GetOwnerProcess(); process != nullptr) { + process->SetRunningThread(core_id, highest_thread, state.idle_count); + } } else { - this->state.idle_count++; + state.idle_count++; } } - this->state.highest_priority_thread = highest_thread; - this->state.needs_scheduling = true; - return (1ULL << this->core_id); + state.highest_priority_thread = highest_thread; + state.needs_scheduling.store(true); + return (1ULL << core_id); } else { return 0; } @@ -90,16 +94,29 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) { ClearSchedulerUpdateNeeded(kernel); u64 cores_needing_scheduling = 0, idle_cores = 0; - Thread* top_threads[Core::Hardware::NUM_CPU_CORES]; + KThread* top_threads[Core::Hardware::NUM_CPU_CORES]; auto& priority_queue = GetPriorityQueue(kernel); /// We want to go over all cores, finding the highest priority thread and determining if /// scheduling is needed for that core. for (size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { - Thread* top_thread = priority_queue.GetScheduledFront(static_cast<s32>(core_id)); + KThread* top_thread = priority_queue.GetScheduledFront(static_cast<s32>(core_id)); if (top_thread != nullptr) { // If the thread has no waiters, we need to check if the process has a thread pinned. - // TODO(bunnei): Implement thread pinning + if (top_thread->GetNumKernelWaiters() == 0) { + if (Process* parent = top_thread->GetOwnerProcess(); parent != nullptr) { + if (KThread* pinned = parent->GetPinnedThread(static_cast<s32>(core_id)); + pinned != nullptr && pinned != top_thread) { + // We prefer our parent's pinned thread if possible. However, we also don't + // want to schedule un-runnable threads. + if (pinned->GetRawState() == ThreadState::Runnable) { + top_thread = pinned; + } else { + top_thread = nullptr; + } + } + } + } } else { idle_cores |= (1ULL << core_id); } @@ -112,7 +129,7 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) { // Idle cores are bad. We're going to try to migrate threads to each idle core in turn. while (idle_cores != 0) { const auto core_id = static_cast<u32>(std::countr_zero(idle_cores)); - if (Thread* suggested = priority_queue.GetSuggestedFront(core_id); suggested != nullptr) { + if (KThread* suggested = priority_queue.GetSuggestedFront(core_id); suggested != nullptr) { s32 migration_candidates[Core::Hardware::NUM_CPU_CORES]; size_t num_candidates = 0; @@ -120,7 +137,7 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) { while (suggested != nullptr) { // Check if the suggested thread is the top thread on its core. const s32 suggested_core = suggested->GetActiveCore(); - if (Thread* top_thread = + if (KThread* top_thread = (suggested_core >= 0) ? top_threads[suggested_core] : nullptr; top_thread != suggested) { // Make sure we're not dealing with threads too high priority for migration. @@ -152,7 +169,7 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) { // Check if there's some other thread that can run on the candidate core. const s32 candidate_core = migration_candidates[i]; suggested = top_threads[candidate_core]; - if (Thread* next_on_candidate_core = + if (KThread* next_on_candidate_core = priority_queue.GetScheduledNext(candidate_core, suggested); next_on_candidate_core != nullptr) { // The candidate core can run some other thread! We'll migrate its current @@ -182,7 +199,20 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) { return cores_needing_scheduling; } -void KScheduler::OnThreadStateChanged(KernelCore& kernel, Thread* thread, ThreadState old_state) { +void KScheduler::ClearPreviousThread(KernelCore& kernel, KThread* thread) { + ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; ++i) { + // Get an atomic reference to the core scheduler's previous thread. + std::atomic_ref<KThread*> prev_thread(kernel.Scheduler(static_cast<s32>(i)).prev_thread); + static_assert(std::atomic_ref<KThread*>::is_always_lock_free); + + // Atomically clear the previous thread if it's our target. + KThread* compare = thread; + prev_thread.compare_exchange_strong(compare, nullptr); + } +} + +void KScheduler::OnThreadStateChanged(KernelCore& kernel, KThread* thread, ThreadState old_state) { ASSERT(kernel.GlobalSchedulerContext().IsLocked()); // Check if the state has changed, because if it hasn't there's nothing to do. @@ -205,7 +235,7 @@ void KScheduler::OnThreadStateChanged(KernelCore& kernel, Thread* thread, Thread } } -void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, s32 old_priority) { +void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s32 old_priority) { ASSERT(kernel.GlobalSchedulerContext().IsLocked()); // If the thread is runnable, we want to change its priority in the queue. @@ -217,7 +247,7 @@ void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, Thread* thread, s32 } } -void KScheduler::OnThreadAffinityMaskChanged(KernelCore& kernel, Thread* thread, +void KScheduler::OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread, const KAffinityMask& old_affinity, s32 old_core) { ASSERT(kernel.GlobalSchedulerContext().IsLocked()); @@ -237,8 +267,8 @@ void KScheduler::RotateScheduledQueue(s32 core_id, s32 priority) { auto& priority_queue = GetPriorityQueue(kernel); // Rotate the front of the queue to the end. - Thread* top_thread = priority_queue.GetScheduledFront(core_id, priority); - Thread* next_thread = nullptr; + KThread* top_thread = priority_queue.GetScheduledFront(core_id, priority); + KThread* next_thread = nullptr; if (top_thread != nullptr) { next_thread = priority_queue.MoveToScheduledBack(top_thread); if (next_thread != top_thread) { @@ -249,11 +279,11 @@ void KScheduler::RotateScheduledQueue(s32 core_id, s32 priority) { // While we have a suggested thread, try to migrate it! { - Thread* suggested = priority_queue.GetSuggestedFront(core_id, priority); + KThread* suggested = priority_queue.GetSuggestedFront(core_id, priority); while (suggested != nullptr) { // Check if the suggested thread is the top thread on its core. const s32 suggested_core = suggested->GetActiveCore(); - if (Thread* top_on_suggested_core = + if (KThread* top_on_suggested_core = (suggested_core >= 0) ? priority_queue.GetScheduledFront(suggested_core) : nullptr; top_on_suggested_core != suggested) { @@ -285,7 +315,7 @@ void KScheduler::RotateScheduledQueue(s32 core_id, s32 priority) { // Now that we might have migrated a thread with the same priority, check if we can do better. { - Thread* best_thread = priority_queue.GetScheduledFront(core_id); + KThread* best_thread = priority_queue.GetScheduledFront(core_id); if (best_thread == GetCurrentThread()) { best_thread = priority_queue.GetScheduledNext(core_id, best_thread); } @@ -293,7 +323,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) { - Thread* suggested = priority_queue.GetSuggestedFront(core_id); + KThread* suggested = priority_queue.GetSuggestedFront(core_id); while (suggested != nullptr) { // If the suggestion's priority is the same as ours, don't bother. if (suggested->GetPriority() >= best_thread->GetPriority()) { @@ -302,7 +332,7 @@ void KScheduler::RotateScheduledQueue(s32 core_id, s32 priority) { // Check if the suggested thread is the top thread on its core. const s32 suggested_core = suggested->GetActiveCore(); - if (Thread* top_on_suggested_core = + if (KThread* top_on_suggested_core = (suggested_core >= 0) ? priority_queue.GetScheduledFront(suggested_core) : nullptr; top_on_suggested_core != suggested) { @@ -352,12 +382,14 @@ void KScheduler::DisableScheduling(KernelCore& kernel) { } } -void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling, - Core::EmuThreadHandle global_thread) { +void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) { if (auto* scheduler = kernel.CurrentScheduler(); scheduler) { - scheduler->GetCurrentThread()->EnableDispatch(); + ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1); + if (scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1) { + scheduler->GetCurrentThread()->EnableDispatch(); + } } - RescheduleCores(kernel, cores_needing_scheduling, global_thread); + RescheduleCores(kernel, cores_needing_scheduling); } u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) { @@ -372,15 +404,13 @@ KSchedulerPriorityQueue& KScheduler::GetPriorityQueue(KernelCore& kernel) { return kernel.GlobalSchedulerContext().priority_queue; } -void KScheduler::YieldWithoutCoreMigration() { - auto& kernel = system.Kernel(); - +void KScheduler::YieldWithoutCoreMigration(KernelCore& kernel) { // Validate preconditions. ASSERT(CanSchedule(kernel)); ASSERT(kernel.CurrentProcess() != nullptr); // Get the current thread and process. - Thread& cur_thread = *GetCurrentThread(); + KThread& cur_thread = Kernel::GetCurrentThread(kernel); Process& cur_process = *kernel.CurrentProcess(); // If the thread's yield count matches, there's nothing for us to do. @@ -398,7 +428,7 @@ void KScheduler::YieldWithoutCoreMigration() { const auto cur_state = cur_thread.GetRawState(); if (cur_state == ThreadState::Runnable) { // Put the current thread at the back of the queue. - Thread* next_thread = priority_queue.MoveToScheduledBack(std::addressof(cur_thread)); + KThread* next_thread = priority_queue.MoveToScheduledBack(std::addressof(cur_thread)); IncrementScheduledCount(std::addressof(cur_thread)); // If the next thread is different, we have an update to perform. @@ -413,15 +443,13 @@ void KScheduler::YieldWithoutCoreMigration() { } } -void KScheduler::YieldWithCoreMigration() { - auto& kernel = system.Kernel(); - +void KScheduler::YieldWithCoreMigration(KernelCore& kernel) { // Validate preconditions. ASSERT(CanSchedule(kernel)); ASSERT(kernel.CurrentProcess() != nullptr); // Get the current thread and process. - Thread& cur_thread = *GetCurrentThread(); + KThread& cur_thread = Kernel::GetCurrentThread(kernel); Process& cur_process = *kernel.CurrentProcess(); // If the thread's yield count matches, there's nothing for us to do. @@ -442,17 +470,17 @@ void KScheduler::YieldWithCoreMigration() { const s32 core_id = cur_thread.GetActiveCore(); // Put the current thread at the back of the queue. - Thread* next_thread = priority_queue.MoveToScheduledBack(std::addressof(cur_thread)); + KThread* next_thread = priority_queue.MoveToScheduledBack(std::addressof(cur_thread)); IncrementScheduledCount(std::addressof(cur_thread)); // While we have a suggested thread, try to migrate it! bool recheck = false; - Thread* suggested = priority_queue.GetSuggestedFront(core_id); + KThread* suggested = priority_queue.GetSuggestedFront(core_id); while (suggested != nullptr) { // Check if the suggested thread is the thread running on its core. const s32 suggested_core = suggested->GetActiveCore(); - if (Thread* running_on_suggested_core = + if (KThread* running_on_suggested_core = (suggested_core >= 0) ? kernel.Scheduler(suggested_core).state.highest_priority_thread : nullptr; @@ -503,15 +531,13 @@ void KScheduler::YieldWithCoreMigration() { } } -void KScheduler::YieldToAnyThread() { - auto& kernel = system.Kernel(); - +void KScheduler::YieldToAnyThread(KernelCore& kernel) { // Validate preconditions. ASSERT(CanSchedule(kernel)); ASSERT(kernel.CurrentProcess() != nullptr); // Get the current thread and process. - Thread& cur_thread = *GetCurrentThread(); + KThread& cur_thread = Kernel::GetCurrentThread(kernel); Process& cur_process = *kernel.CurrentProcess(); // If the thread's yield count matches, there's nothing for us to do. @@ -539,11 +565,11 @@ void KScheduler::YieldToAnyThread() { // If there's nothing scheduled, we can try to perform a migration. if (priority_queue.GetScheduledFront(core_id) == nullptr) { // While we have a suggested thread, try to migrate it! - Thread* suggested = priority_queue.GetSuggestedFront(core_id); + KThread* suggested = priority_queue.GetSuggestedFront(core_id); while (suggested != nullptr) { // Check if the suggested thread is the top thread on its core. const s32 suggested_core = suggested->GetActiveCore(); - if (Thread* top_on_suggested_core = + if (KThread* top_on_suggested_core = (suggested_core >= 0) ? priority_queue.GetScheduledFront(suggested_core) : nullptr; top_on_suggested_core != suggested) { @@ -581,22 +607,21 @@ void KScheduler::YieldToAnyThread() { } } -KScheduler::KScheduler(Core::System& system, std::size_t core_id) - : system(system), core_id(core_id) { +KScheduler::KScheduler(Core::System& system, s32 core_id) : system(system), core_id(core_id) { switch_fiber = std::make_shared<Common::Fiber>(OnSwitch, this); - this->state.needs_scheduling = true; - this->state.interrupt_task_thread_runnable = false; - this->state.should_count_idle = false; - this->state.idle_count = 0; - this->state.idle_thread_stack = nullptr; - this->state.highest_priority_thread = nullptr; + state.needs_scheduling.store(true); + state.interrupt_task_thread_runnable = false; + state.should_count_idle = false; + state.idle_count = 0; + state.idle_thread_stack = nullptr; + state.highest_priority_thread = nullptr; } KScheduler::~KScheduler() = default; -Thread* KScheduler::GetCurrentThread() const { - if (current_thread) { - return current_thread; +KThread* KScheduler::GetCurrentThread() const { + if (auto result = current_thread.load(); result) { + return result; } return idle_thread; } @@ -613,7 +638,7 @@ void KScheduler::RescheduleCurrentCore() { phys_core.ClearInterrupt(); } guard.lock(); - if (this->state.needs_scheduling) { + if (state.needs_scheduling.load()) { Schedule(); } else { guard.unlock(); @@ -624,66 +649,76 @@ void KScheduler::OnThreadStart() { SwitchContextStep2(); } -void KScheduler::Unload(Thread* thread) { +void KScheduler::Unload(KThread* thread) { + LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr"); + if (thread) { - thread->SetIsRunning(false); - if (thread->IsContinuousOnSVC() && !thread->IsHLEThread()) { + if (thread->IsCallingSvc()) { system.ArmInterface(core_id).ExceptionalExit(); - thread->SetContinuousOnSVC(false); + thread->ClearIsCallingSvc(); } - if (!thread->IsHLEThread() && !thread->HasExited()) { + if (!thread->IsTerminationRequested()) { + prev_thread = thread; + Core::ARM_Interface& cpu_core = system.ArmInterface(core_id); cpu_core.SaveContext(thread->GetContext32()); cpu_core.SaveContext(thread->GetContext64()); // Save the TPIDR_EL0 system register in case it was modified. thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0()); cpu_core.ClearExclusiveState(); + } else { + prev_thread = nullptr; } thread->context_guard.unlock(); } } -void KScheduler::Reload(Thread* thread) { +void KScheduler::Reload(KThread* thread) { + LOG_TRACE(Kernel, "core {}, reload thread {}", core_id, thread ? thread->GetName() : "nullptr"); + if (thread) { ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable."); - // Cancel any outstanding wakeup events for this thread - thread->SetIsRunning(true); - thread->SetWasRunning(false); - auto* const thread_owner_process = thread->GetOwnerProcess(); if (thread_owner_process != nullptr) { system.Kernel().MakeCurrentProcess(thread_owner_process); } - if (!thread->IsHLEThread()) { - Core::ARM_Interface& cpu_core = system.ArmInterface(core_id); - cpu_core.LoadContext(thread->GetContext32()); - cpu_core.LoadContext(thread->GetContext64()); - cpu_core.SetTlsAddress(thread->GetTLSAddress()); - cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0()); - cpu_core.ClearExclusiveState(); - } + + Core::ARM_Interface& cpu_core = system.ArmInterface(core_id); + cpu_core.LoadContext(thread->GetContext32()); + cpu_core.LoadContext(thread->GetContext64()); + cpu_core.SetTlsAddress(thread->GetTLSAddress()); + cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0()); + cpu_core.ClearExclusiveState(); } } void KScheduler::SwitchContextStep2() { // Load context of new thread - Reload(current_thread); + Reload(current_thread.load()); RescheduleCurrentCore(); } void KScheduler::ScheduleImpl() { - Thread* previous_thread = current_thread; - current_thread = state.highest_priority_thread; + KThread* previous_thread = current_thread.load(); + KThread* next_thread = state.highest_priority_thread; - this->state.needs_scheduling = false; + state.needs_scheduling = false; + + // We never want to schedule a null thread, so use the idle thread if we don't have a next. + if (next_thread == nullptr) { + next_thread = idle_thread; + } - if (current_thread == previous_thread) { + // If we're not actually switching thread, there's nothing to do. + if (next_thread == current_thread.load()) { guard.unlock(); return; } + current_thread.store(next_thread); + Process* const previous_process = system.Kernel().CurrentProcess(); UpdateLastContextSwitchTime(previous_thread, previous_process); @@ -714,28 +749,29 @@ void KScheduler::SwitchToCurrent() { while (true) { { std::scoped_lock lock{guard}; - current_thread = state.highest_priority_thread; - this->state.needs_scheduling = false; + current_thread.store(state.highest_priority_thread); + state.needs_scheduling.store(false); } const auto is_switch_pending = [this] { std::scoped_lock lock{guard}; - return state.needs_scheduling.load(std::memory_order_relaxed); + return state.needs_scheduling.load(); }; do { - if (current_thread != nullptr && !current_thread->IsHLEThread()) { - current_thread->context_guard.lock(); - if (current_thread->GetRawState() != ThreadState::Runnable) { - current_thread->context_guard.unlock(); + auto next_thread = current_thread.load(); + if (next_thread != nullptr) { + next_thread->context_guard.lock(); + if (next_thread->GetRawState() != ThreadState::Runnable) { + next_thread->context_guard.unlock(); break; } - if (static_cast<u32>(current_thread->GetProcessorID()) != core_id) { - current_thread->context_guard.unlock(); + if (next_thread->GetActiveCore() != core_id) { + next_thread->context_guard.unlock(); break; } } std::shared_ptr<Common::Fiber>* next_context; - if (current_thread != nullptr) { - next_context = ¤t_thread->GetHostContext(); + if (next_thread != nullptr) { + next_context = &next_thread->GetHostContext(); } else { next_context = &idle_thread->GetHostContext(); } @@ -744,13 +780,13 @@ void KScheduler::SwitchToCurrent() { } } -void KScheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) { +void KScheduler::UpdateLastContextSwitchTime(KThread* thread, Process* process) { const u64 prev_switch_ticks = last_context_switch_time; const u64 most_recent_switch_ticks = system.CoreTiming().GetCPUTicks(); const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks; if (thread != nullptr) { - thread->UpdateCPUTimeTicks(update_ticks); + thread->AddCpuTime(core_id, update_ticks); } if (process != nullptr) { @@ -764,15 +800,10 @@ void KScheduler::Initialize() { std::string name = "Idle Thread Id:" + std::to_string(core_id); std::function<void(void*)> init_func = Core::CpuManager::GetIdleThreadStartFunc(); void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); - ThreadType type = static_cast<ThreadType>(THREADTYPE_KERNEL | THREADTYPE_HLE | THREADTYPE_IDLE); - auto thread_res = Thread::Create(system, type, name, 0, 64, 0, static_cast<u32>(core_id), 0, - nullptr, std::move(init_func), init_func_parameter); + auto thread_res = KThread::Create(system, ThreadType::Main, name, 0, + KThread::IdleThreadPriority, 0, static_cast<u32>(core_id), 0, + nullptr, std::move(init_func), init_func_parameter); idle_thread = thread_res.Unwrap().get(); - - { - KScopedSchedulerLock lock{system.Kernel()}; - idle_thread->SetState(ThreadState::Runnable); - } } KScopedSchedulerLock::KScopedSchedulerLock(KernelCore& kernel) diff --git a/src/core/hle/kernel/k_scheduler.h b/src/core/hle/kernel/k_scheduler.h index 783665123..f595b9a5c 100644 --- a/src/core/hle/kernel/k_scheduler.h +++ b/src/core/hle/kernel/k_scheduler.h @@ -29,29 +29,33 @@ namespace Kernel { class KernelCore; class Process; class SchedulerLock; -class Thread; +class KThread; class KScheduler final { public: - explicit KScheduler(Core::System& system, std::size_t core_id); + explicit KScheduler(Core::System& system, s32 core_id); ~KScheduler(); /// Reschedules to the next available thread (call after current thread is suspended) void RescheduleCurrentCore(); /// Reschedules cores pending reschedule, to be called on EnableScheduling. - static void RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule, - Core::EmuThreadHandle global_thread); + static void RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule); /// The next two are for SingleCore Only. /// Unload current thread before preempting core. - void Unload(Thread* thread); + void Unload(KThread* thread); /// Reload current thread after core preemption. - void Reload(Thread* thread); + void Reload(KThread* thread); /// Gets the current running thread - [[nodiscard]] Thread* GetCurrentThread() const; + [[nodiscard]] KThread* GetCurrentThread() const; + + /// Returns true if the scheduler is idle + [[nodiscard]] bool IsIdle() const { + return GetCurrentThread() == idle_thread; + } /// Gets the timestamp for the last context switch in ticks. [[nodiscard]] u64 GetLastContextSwitchTicks() const; @@ -72,14 +76,14 @@ public: return switch_fiber; } - [[nodiscard]] u64 UpdateHighestPriorityThread(Thread* highest_thread); + [[nodiscard]] u64 UpdateHighestPriorityThread(KThread* highest_thread); /** * Takes a thread and moves it to the back of the it's priority list. * * @note This operation can be redundant and no scheduling is changed if marked as so. */ - void YieldWithoutCoreMigration(); + static void YieldWithoutCoreMigration(KernelCore& kernel); /** * Takes a thread and moves it to the back of the it's priority list. @@ -88,7 +92,7 @@ public: * * @note This operation can be redundant and no scheduling is changed if marked as so. */ - void YieldWithCoreMigration(); + static void YieldWithCoreMigration(KernelCore& kernel); /** * Takes a thread and moves it out of the scheduling queue. @@ -97,16 +101,18 @@ public: * * @note This operation can be redundant and no scheduling is changed if marked as so. */ - void YieldToAnyThread(); + static void YieldToAnyThread(KernelCore& kernel); + + static void ClearPreviousThread(KernelCore& kernel, KThread* thread); /// Notify the scheduler a thread's status has changed. - static void OnThreadStateChanged(KernelCore& kernel, Thread* thread, ThreadState old_state); + static void OnThreadStateChanged(KernelCore& kernel, KThread* thread, ThreadState 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, KThread* thread, s32 old_priority); /// Notify the scheduler a thread's core and/or affinity mask has changed. - static void OnThreadAffinityMaskChanged(KernelCore& kernel, Thread* thread, + static void OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread, const KAffinityMask& old_affinity, s32 old_core); static bool CanSchedule(KernelCore& kernel); @@ -114,8 +120,7 @@ public: static void SetSchedulerUpdateNeeded(KernelCore& kernel); static void ClearSchedulerUpdateNeeded(KernelCore& kernel); static void DisableScheduling(KernelCore& kernel); - static void EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling, - Core::EmuThreadHandle global_thread); + static void EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling); [[nodiscard]] static u64 UpdateHighestPriorityThreads(KernelCore& kernel); private: @@ -163,13 +168,15 @@ private: * most recent tick count retrieved. No special arithmetic is * applied to it. */ - void UpdateLastContextSwitchTime(Thread* thread, Process* process); + void UpdateLastContextSwitchTime(KThread* thread, Process* process); static void OnSwitch(void* this_scheduler); void SwitchToCurrent(); - Thread* current_thread{}; - Thread* idle_thread{}; + KThread* prev_thread{}; + std::atomic<KThread*> current_thread{}; + + KThread* idle_thread; std::shared_ptr<Common::Fiber> switch_fiber{}; @@ -178,7 +185,7 @@ private: bool interrupt_task_thread_runnable{}; bool should_count_idle{}; u64 idle_count{}; - Thread* highest_priority_thread{}; + KThread* highest_priority_thread{}; void* idle_thread_stack{}; }; @@ -186,7 +193,7 @@ private: Core::System& system; u64 last_context_switch_time{}; - const std::size_t core_id; + const s32 core_id; Common::SpinLock guard{}; }; diff --git a/src/core/hle/kernel/k_scheduler_lock.h b/src/core/hle/kernel/k_scheduler_lock.h index 9b40bd22c..169455d18 100644 --- a/src/core/hle/kernel/k_scheduler_lock.h +++ b/src/core/hle/kernel/k_scheduler_lock.h @@ -10,6 +10,7 @@ #include "common/assert.h" #include "common/spin_lock.h" #include "core/hardware_properties.h" +#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" namespace Kernel { @@ -22,46 +23,45 @@ public: explicit KAbstractSchedulerLock(KernelCore& kernel_) : kernel{kernel_} {} bool IsLockedByCurrentThread() const { - return this->owner_thread == kernel.GetCurrentEmuThreadID(); + return owner_thread == GetCurrentThreadPointer(kernel); } void Lock() { - if (this->IsLockedByCurrentThread()) { + if (IsLockedByCurrentThread()) { // If we already own the lock, we can just increment the count. - ASSERT(this->lock_count > 0); - this->lock_count++; + ASSERT(lock_count > 0); + lock_count++; } else { // Otherwise, we want to disable scheduling and acquire the spinlock. SchedulerType::DisableScheduling(kernel); - this->spin_lock.lock(); + spin_lock.lock(); // For debug, ensure that our state is valid. - ASSERT(this->lock_count == 0); - ASSERT(this->owner_thread == Core::EmuThreadHandle::InvalidHandle()); + ASSERT(lock_count == 0); + ASSERT(owner_thread == nullptr); // Increment count, take ownership. - this->lock_count = 1; - this->owner_thread = kernel.GetCurrentEmuThreadID(); + lock_count = 1; + owner_thread = GetCurrentThreadPointer(kernel); } } void Unlock() { - ASSERT(this->IsLockedByCurrentThread()); - ASSERT(this->lock_count > 0); + ASSERT(IsLockedByCurrentThread()); + ASSERT(lock_count > 0); // Release an instance of the lock. - if ((--this->lock_count) == 0) { + if ((--lock_count) == 0) { // We're no longer going to hold the lock. Take note of what cores need scheduling. const u64 cores_needing_scheduling = SchedulerType::UpdateHighestPriorityThreads(kernel); - Core::EmuThreadHandle leaving_thread = owner_thread; // Note that we no longer hold the lock, and unlock the spinlock. - this->owner_thread = Core::EmuThreadHandle::InvalidHandle(); - this->spin_lock.unlock(); + owner_thread = nullptr; + spin_lock.unlock(); // Enable scheduling, and perform a rescheduling operation. - SchedulerType::EnableScheduling(kernel, cores_needing_scheduling, leaving_thread); + SchedulerType::EnableScheduling(kernel, cores_needing_scheduling); } } @@ -69,7 +69,7 @@ private: KernelCore& kernel; Common::SpinLock spin_lock{}; s32 lock_count{}; - Core::EmuThreadHandle owner_thread{Core::EmuThreadHandle::InvalidHandle()}; + KThread* owner_thread{}; }; } // namespace Kernel diff --git a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h index 2bb3817fa..f8189e107 100644 --- a/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h +++ b/src/core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h @@ -9,27 +9,24 @@ #include "common/common_types.h" #include "core/hle/kernel/handle_table.h" +#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/thread.h" #include "core/hle/kernel/time_manager.h" namespace Kernel { class KScopedSchedulerLockAndSleep { public: - explicit KScopedSchedulerLockAndSleep(KernelCore& kernel, Handle& event_handle, Thread* t, - s64 timeout) - : kernel(kernel), event_handle(event_handle), thread(t), timeout_tick(timeout) { - event_handle = InvalidHandle; - + explicit KScopedSchedulerLockAndSleep(KernelCore& kernel, KThread* t, s64 timeout) + : kernel(kernel), thread(t), timeout_tick(timeout) { // Lock the scheduler. kernel.GlobalSchedulerContext().scheduler_lock.Lock(); } ~KScopedSchedulerLockAndSleep() { // Register the sleep. - if (this->timeout_tick > 0) { - kernel.TimeManager().ScheduleTimeEvent(event_handle, this->thread, this->timeout_tick); + if (timeout_tick > 0) { + kernel.TimeManager().ScheduleTimeEvent(thread, timeout_tick); } // Unlock the scheduler. @@ -37,13 +34,12 @@ public: } void CancelSleep() { - this->timeout_tick = 0; + timeout_tick = 0; } private: KernelCore& kernel; - Handle& event_handle; - Thread* thread{}; + KThread* thread{}; s64 timeout_tick{}; }; diff --git a/src/core/hle/kernel/k_synchronization_object.cpp b/src/core/hle/kernel/k_synchronization_object.cpp index 1c508cb55..a3b34f82f 100644 --- a/src/core/hle/kernel/k_synchronization_object.cpp +++ b/src/core/hle/kernel/k_synchronization_object.cpp @@ -7,9 +7,9 @@ #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/k_thread.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/svc_results.h" -#include "core/hle/kernel/thread.h" namespace Kernel { @@ -20,12 +20,11 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index, std::vector<ThreadListNode> thread_nodes(num_objects); // Prepare for wait. - Thread* thread = kernel.CurrentScheduler()->GetCurrentThread(); - Handle timer = InvalidHandle; + KThread* thread = kernel.CurrentScheduler()->GetCurrentThread(); { // Setup the scheduling lock and sleep. - KScopedSchedulerLockAndSleep slp(kernel, timer, thread, timeout); + KScopedSchedulerLockAndSleep slp{kernel, thread, timeout}; // Check if any of the objects are already signaled. for (auto i = 0; i < num_objects; ++i) { @@ -90,10 +89,7 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index, thread->SetWaitObjectsForDebugging({}); // Cancel the timer as needed. - if (timer != InvalidHandle) { - auto& time_manager = kernel.TimeManager(); - time_manager.UnscheduleTimeEvent(timer); - } + kernel.TimeManager().UnscheduleTimeEvent(thread); // Get the wait result. ResultCode wait_result{RESULT_SUCCESS}; @@ -136,7 +132,7 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index, KSynchronizationObject::KSynchronizationObject(KernelCore& kernel) : Object{kernel} {} -KSynchronizationObject ::~KSynchronizationObject() = default; +KSynchronizationObject::~KSynchronizationObject() = default; void KSynchronizationObject::NotifyAvailable(ResultCode result) { KScopedSchedulerLock lock(kernel); @@ -148,7 +144,7 @@ void KSynchronizationObject::NotifyAvailable(ResultCode result) { // Iterate over each thread. for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) { - Thread* thread = cur_node->thread; + KThread* thread = cur_node->thread; if (thread->GetState() == ThreadState::Waiting) { thread->SetSyncedObject(this, result); thread->SetState(ThreadState::Runnable); @@ -156,8 +152,8 @@ void KSynchronizationObject::NotifyAvailable(ResultCode result) { } } -std::vector<Thread*> KSynchronizationObject::GetWaitingThreadsForDebugging() const { - std::vector<Thread*> threads; +std::vector<KThread*> KSynchronizationObject::GetWaitingThreadsForDebugging() const { + std::vector<KThread*> threads; // If debugging, dump the list of waiters. { diff --git a/src/core/hle/kernel/k_synchronization_object.h b/src/core/hle/kernel/k_synchronization_object.h index 14d80ebf1..f65c71c28 100644 --- a/src/core/hle/kernel/k_synchronization_object.h +++ b/src/core/hle/kernel/k_synchronization_object.h @@ -13,14 +13,14 @@ namespace Kernel { class KernelCore; class Synchronization; -class Thread; +class KThread; /// Class that represents a Kernel object that a thread can be waiting on class KSynchronizationObject : public Object { public: struct ThreadListNode { ThreadListNode* next{}; - Thread* thread{}; + KThread* thread{}; }; [[nodiscard]] static ResultCode Wait(KernelCore& kernel, s32* out_index, @@ -29,7 +29,7 @@ public: [[nodiscard]] virtual bool IsSignaled() const = 0; - [[nodiscard]] std::vector<Thread*> GetWaitingThreadsForDebugging() const; + [[nodiscard]] std::vector<KThread*> GetWaitingThreadsForDebugging() const; protected: explicit KSynchronizationObject(KernelCore& kernel); diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp new file mode 100644 index 000000000..aa100e139 --- /dev/null +++ b/src/core/hle/kernel/k_thread.cpp @@ -0,0 +1,1050 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <cinttypes> +#include <optional> +#include <vector> + +#include "common/assert.h" +#include "common/bit_util.h" +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/fiber.h" +#include "common/logging/log.h" +#include "common/scope_exit.h" +#include "common/thread_queue_list.h" +#include "core/core.h" +#include "core/cpu_manager.h" +#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/k_thread.h" +#include "core/hle/kernel/k_thread_queue.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/resource_limit.h" +#include "core/hle/kernel/svc_results.h" +#include "core/hle/kernel/time_manager.h" +#include "core/hle/result.h" +#include "core/memory.h" + +#ifdef ARCHITECTURE_x86_64 +#include "core/arm/dynarmic/arm_dynarmic_32.h" +#include "core/arm/dynarmic/arm_dynarmic_64.h" +#endif + +namespace { +static void ResetThreadContext32(Core::ARM_Interface::ThreadContext32& context, u32 stack_top, + u32 entry_point, u32 arg) { + context = {}; + context.cpu_registers[0] = arg; + context.cpu_registers[15] = entry_point; + context.cpu_registers[13] = stack_top; +} + +static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context, VAddr stack_top, + VAddr entry_point, u64 arg) { + context = {}; + context.cpu_registers[0] = arg; + context.pc = entry_point; + context.sp = stack_top; + // TODO(merry): Perform a hardware test to determine the below value. + context.fpcr = 0; +} +} // namespace + +namespace Kernel { + +KThread::KThread(KernelCore& kernel) + : KSynchronizationObject{kernel}, activity_pause_lock{kernel} {} +KThread::~KThread() = default; + +ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top, s32 prio, + s32 virt_core, Process* owner, ThreadType type) { + // Assert parameters are valid. + ASSERT((type == ThreadType::Main) || + (Svc::HighestThreadPriority <= prio && prio <= Svc::LowestThreadPriority)); + ASSERT((owner != nullptr) || (type != ThreadType::User)); + ASSERT(0 <= virt_core && virt_core < static_cast<s32>(Common::BitSize<u64>())); + + // Convert the virtual core to a physical core. + const s32 phys_core = Core::Hardware::VirtualToPhysicalCoreMap[virt_core]; + ASSERT(0 <= phys_core && phys_core < static_cast<s32>(Core::Hardware::NUM_CPU_CORES)); + + // First, clear the TLS address. + tls_address = {}; + + // Next, assert things based on the type. + switch (type) { + case ThreadType::Main: + ASSERT(arg == 0); + [[fallthrough]]; + case ThreadType::HighPriority: + [[fallthrough]]; + case ThreadType::User: + ASSERT(((owner == nullptr) || + (owner->GetCoreMask() | (1ULL << virt_core)) == owner->GetCoreMask())); + ASSERT(((owner == nullptr) || + (owner->GetPriorityMask() | (1ULL << prio)) == owner->GetPriorityMask())); + break; + case ThreadType::Kernel: + UNIMPLEMENTED(); + break; + default: + UNREACHABLE_MSG("KThread::Initialize: Unknown ThreadType {}", static_cast<u32>(type)); + break; + } + thread_type_for_debugging = type; + + // Set the ideal core ID and affinity mask. + virtual_ideal_core_id = virt_core; + physical_ideal_core_id = phys_core; + virtual_affinity_mask = 1ULL << virt_core; + physical_affinity_mask.SetAffinity(phys_core, true); + + // Set the thread state. + thread_state = (type == ThreadType::Main) ? ThreadState::Runnable : ThreadState::Initialized; + + // Set TLS address. + tls_address = 0; + + // Set parent and condvar tree. + parent = nullptr; + condvar_tree = nullptr; + + // Set sync booleans. + signaled = false; + termination_requested = false; + wait_cancelled = false; + cancellable = false; + + // Set core ID and wait result. + core_id = phys_core; + wait_result = Svc::ResultNoSynchronizationObject; + + // Set priorities. + priority = prio; + base_priority = prio; + + // Set sync object and waiting lock to null. + synced_object = nullptr; + + // Initialize sleeping queue. + sleeping_queue = nullptr; + + // Set suspend flags. + suspend_request_flags = 0; + suspend_allowed_flags = static_cast<u32>(ThreadState::SuspendFlagMask); + + // We're neither debug attached, nor are we nesting our priority inheritance. + debug_attached = false; + priority_inheritance_count = 0; + + // We haven't been scheduled, and we have done no light IPC. + schedule_count = -1; + last_scheduled_tick = 0; + light_ipc_data = nullptr; + + // We're not waiting for a lock, and we haven't disabled migration. + lock_owner = nullptr; + num_core_migration_disables = 0; + + // We have no waiters, but we do have an entrypoint. + num_kernel_waiters = 0; + + // Set our current core id. + current_core_id = phys_core; + + // We haven't released our resource limit hint, and we've spent no time on the cpu. + resource_limit_release_hint = false; + cpu_time = 0; + + // Clear our stack parameters. + std::memset(static_cast<void*>(std::addressof(GetStackParameters())), 0, + sizeof(StackParameters)); + + // Setup the TLS, if needed. + if (type == ThreadType::User) { + tls_address = owner->CreateTLSRegion(); + } + + // Set parent, if relevant. + if (owner != nullptr) { + parent = owner; + parent->IncrementThreadCount(); + } + + // Initialize thread context. + ResetThreadContext64(thread_context_64, user_stack_top, func, arg); + ResetThreadContext32(thread_context_32, static_cast<u32>(user_stack_top), + static_cast<u32>(func), static_cast<u32>(arg)); + + // Setup the stack parameters. + StackParameters& sp = GetStackParameters(); + sp.cur_thread = this; + sp.disable_count = 1; + SetInExceptionHandler(); + + // Set thread ID. + thread_id = kernel.CreateNewThreadID(); + + // We initialized! + initialized = true; + + // Register ourselves with our parent process. + if (parent != nullptr) { + parent->RegisterThread(this); + if (parent->IsSuspended()) { + RequestSuspend(SuspendType::Process); + } + } + + return RESULT_SUCCESS; +} + +ResultCode KThread::InitializeThread(KThread* thread, KThreadFunction func, uintptr_t arg, + VAddr user_stack_top, s32 prio, s32 core, Process* owner, + ThreadType type) { + // Initialize the thread. + R_TRY(thread->Initialize(func, arg, user_stack_top, prio, core, owner, type)); + + return RESULT_SUCCESS; +} + +void KThread::Finalize() { + // If the thread has an owner process, unregister it. + if (parent != nullptr) { + parent->UnregisterThread(this); + } + + // If the thread has a local region, delete it. + if (tls_address != 0) { + parent->FreeTLSRegion(tls_address); + } + + // Release any waiters. + { + ASSERT(lock_owner == nullptr); + KScopedSchedulerLock sl{kernel}; + + auto it = waiter_list.begin(); + while (it != waiter_list.end()) { + // The thread shouldn't be a kernel waiter. + it->SetLockOwner(nullptr); + it->SetSyncedObject(nullptr, Svc::ResultInvalidState); + it->Wakeup(); + it = waiter_list.erase(it); + } + } + + // Decrement the parent process's thread count. + if (parent != nullptr) { + parent->DecrementThreadCount(); + parent->GetResourceLimit()->Release(ResourceType::Threads, 1); + } +} + +bool KThread::IsSignaled() const { + return signaled; +} + +void KThread::Wakeup() { + KScopedSchedulerLock sl{kernel}; + + if (GetState() == ThreadState::Waiting) { + if (sleeping_queue != nullptr) { + sleeping_queue->WakeupThread(this); + } else { + SetState(ThreadState::Runnable); + } + } +} + +void KThread::StartTermination() { + ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + + // Release user exception and unpin, if relevant. + if (parent != nullptr) { + parent->ReleaseUserException(this); + if (parent->GetPinnedThread(GetCurrentCoreId(kernel)) == this) { + parent->UnpinCurrentThread(); + } + } + + // Set state to terminated. + SetState(ThreadState::Terminated); + + // Clear the thread's status as running in parent. + if (parent != nullptr) { + parent->ClearRunningThread(this); + } + + // Signal. + signaled = true; + NotifyAvailable(); + + // Clear previous thread in KScheduler. + KScheduler::ClearPreviousThread(kernel, this); + + // Register terminated dpc flag. + RegisterDpc(DpcFlag::Terminated); +} + +void KThread::Pin() { + ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + + // Set ourselves as pinned. + GetStackParameters().is_pinned = true; + + // Disable core migration. + ASSERT(num_core_migration_disables == 0); + { + ++num_core_migration_disables; + + // Save our ideal state to restore when we're unpinned. + original_physical_ideal_core_id = physical_ideal_core_id; + original_physical_affinity_mask = physical_affinity_mask; + + // Bind ourselves to this core. + const s32 active_core = GetActiveCore(); + const s32 current_core = GetCurrentCoreId(kernel); + + SetActiveCore(current_core); + physical_ideal_core_id = current_core; + physical_affinity_mask.SetAffinityMask(1ULL << current_core); + + if (active_core != current_core || physical_affinity_mask.GetAffinityMask() != + original_physical_affinity_mask.GetAffinityMask()) { + KScheduler::OnThreadAffinityMaskChanged(kernel, this, original_physical_affinity_mask, + active_core); + } + } + + // Disallow performing thread suspension. + { + // Update our allow flags. + suspend_allowed_flags &= ~(1 << (static_cast<u32>(SuspendType::Thread) + + static_cast<u32>(ThreadState::SuspendShift))); + + // Update our state. + const ThreadState old_state = thread_state; + thread_state = static_cast<ThreadState>(GetSuspendFlags() | + static_cast<u32>(old_state & ThreadState::Mask)); + if (thread_state != old_state) { + KScheduler::OnThreadStateChanged(kernel, this, old_state); + } + } + + // TODO(bunnei): Update our SVC access permissions. + ASSERT(parent != nullptr); +} + +void KThread::Unpin() { + ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + + // Set ourselves as unpinned. + GetStackParameters().is_pinned = false; + + // Enable core migration. + ASSERT(num_core_migration_disables == 1); + { + num_core_migration_disables--; + + // Restore our original state. + const KAffinityMask old_mask = physical_affinity_mask; + + physical_ideal_core_id = original_physical_ideal_core_id; + physical_affinity_mask = original_physical_affinity_mask; + + if (physical_affinity_mask.GetAffinityMask() != old_mask.GetAffinityMask()) { + const s32 active_core = GetActiveCore(); + + if (!physical_affinity_mask.GetAffinity(active_core)) { + if (physical_ideal_core_id >= 0) { + SetActiveCore(physical_ideal_core_id); + } else { + SetActiveCore(static_cast<s32>( + Common::BitSize<u64>() - 1 - + std::countl_zero(physical_affinity_mask.GetAffinityMask()))); + } + } + KScheduler::OnThreadAffinityMaskChanged(kernel, this, old_mask, active_core); + } + } + + // Allow performing thread suspension (if termination hasn't been requested). + { + // Update our allow flags. + if (!IsTerminationRequested()) { + suspend_allowed_flags |= (1 << (static_cast<u32>(SuspendType::Thread) + + static_cast<u32>(ThreadState::SuspendShift))); + } + + // Update our state. + const ThreadState old_state = thread_state; + thread_state = static_cast<ThreadState>(GetSuspendFlags() | + static_cast<u32>(old_state & ThreadState::Mask)); + if (thread_state != old_state) { + KScheduler::OnThreadStateChanged(kernel, this, old_state); + } + } + + // TODO(bunnei): Update our SVC access permissions. + ASSERT(parent != nullptr); + + // Resume any threads that began waiting on us while we were pinned. + for (auto it = pinned_waiter_list.begin(); it != pinned_waiter_list.end(); ++it) { + if (it->GetState() == ThreadState::Waiting) { + it->SetState(ThreadState::Runnable); + } + } +} + +ResultCode KThread::GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask) { + KScopedSchedulerLock sl{kernel}; + + // Get the virtual mask. + *out_ideal_core = virtual_ideal_core_id; + *out_affinity_mask = virtual_affinity_mask; + + return RESULT_SUCCESS; +} + +ResultCode KThread::GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask) { + KScopedSchedulerLock sl{kernel}; + ASSERT(num_core_migration_disables >= 0); + + // Select between core mask and original core mask. + if (num_core_migration_disables == 0) { + *out_ideal_core = physical_ideal_core_id; + *out_affinity_mask = physical_affinity_mask.GetAffinityMask(); + } else { + *out_ideal_core = original_physical_ideal_core_id; + *out_affinity_mask = original_physical_affinity_mask.GetAffinityMask(); + } + + return RESULT_SUCCESS; +} + +ResultCode KThread::SetCoreMask(s32 core_id, u64 v_affinity_mask) { + ASSERT(parent != nullptr); + ASSERT(v_affinity_mask != 0); + KScopedLightLock lk{activity_pause_lock}; + + // Set the core mask. + u64 p_affinity_mask = 0; + { + KScopedSchedulerLock sl{kernel}; + ASSERT(num_core_migration_disables >= 0); + + // If the core id is no-update magic, preserve the ideal core id. + if (core_id == Svc::IdealCoreNoUpdate) { + core_id = virtual_ideal_core_id; + R_UNLESS(((1ULL << core_id) & v_affinity_mask) != 0, Svc::ResultInvalidCombination); + } + + // Set the virtual core/affinity mask. + virtual_ideal_core_id = core_id; + virtual_affinity_mask = v_affinity_mask; + + // Translate the virtual core to a physical core. + if (core_id >= 0) { + core_id = Core::Hardware::VirtualToPhysicalCoreMap[core_id]; + } + + // Translate the virtual affinity mask to a physical one. + while (v_affinity_mask != 0) { + const u64 next = std::countr_zero(v_affinity_mask); + v_affinity_mask &= ~(1ULL << next); + p_affinity_mask |= (1ULL << Core::Hardware::VirtualToPhysicalCoreMap[next]); + } + + // If we haven't disabled migration, perform an affinity change. + if (num_core_migration_disables == 0) { + const KAffinityMask old_mask = physical_affinity_mask; + + // Set our new ideals. + physical_ideal_core_id = core_id; + physical_affinity_mask.SetAffinityMask(p_affinity_mask); + + if (physical_affinity_mask.GetAffinityMask() != old_mask.GetAffinityMask()) { + const s32 active_core = GetActiveCore(); + + if (active_core >= 0 && !physical_affinity_mask.GetAffinity(active_core)) { + const s32 new_core = static_cast<s32>( + physical_ideal_core_id >= 0 + ? physical_ideal_core_id + : Common::BitSize<u64>() - 1 - + std::countl_zero(physical_affinity_mask.GetAffinityMask())); + SetActiveCore(new_core); + } + KScheduler::OnThreadAffinityMaskChanged(kernel, this, old_mask, active_core); + } + } else { + // Otherwise, we edit the original affinity for restoration later. + original_physical_ideal_core_id = core_id; + original_physical_affinity_mask.SetAffinityMask(p_affinity_mask); + } + } + + // Update the pinned waiter list. + { + bool retry_update{}; + bool thread_is_pinned{}; + do { + // Lock the scheduler. + KScopedSchedulerLock sl{kernel}; + + // Don't do any further management if our termination has been requested. + R_SUCCEED_IF(IsTerminationRequested()); + + // By default, we won't need to retry. + retry_update = false; + + // Check if the thread is currently running. + bool thread_is_current{}; + s32 thread_core; + for (thread_core = 0; thread_core < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); + ++thread_core) { + if (kernel.Scheduler(thread_core).GetCurrentThread() == this) { + thread_is_current = true; + break; + } + } + + // If the thread is currently running, check whether it's no longer allowed under the + // new mask. + if (thread_is_current && ((1ULL << thread_core) & p_affinity_mask) == 0) { + // If the thread is pinned, we want to wait until it's not pinned. + if (GetStackParameters().is_pinned) { + // Verify that the current thread isn't terminating. + R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), + Svc::ResultTerminationRequested); + + // Note that the thread was pinned. + thread_is_pinned = true; + + // Wait until the thread isn't pinned any more. + pinned_waiter_list.push_back(GetCurrentThread(kernel)); + GetCurrentThread(kernel).SetState(ThreadState::Waiting); + } else { + // If the thread isn't pinned, release the scheduler lock and retry until it's + // not current. + retry_update = true; + } + } + } while (retry_update); + + // If the thread was pinned, it no longer is, and we should remove the current thread from + // our waiter list. + if (thread_is_pinned) { + // Lock the scheduler. + KScopedSchedulerLock sl{kernel}; + + // Remove from the list. + pinned_waiter_list.erase(pinned_waiter_list.iterator_to(GetCurrentThread(kernel))); + } + } + + return RESULT_SUCCESS; +} + +void KThread::SetBasePriority(s32 value) { + ASSERT(Svc::HighestThreadPriority <= value && value <= Svc::LowestThreadPriority); + + KScopedSchedulerLock sl{kernel}; + + // Change our base priority. + base_priority = value; + + // Perform a priority restoration. + RestorePriority(kernel, this); +} + +void KThread::RequestSuspend(SuspendType type) { + KScopedSchedulerLock sl{kernel}; + + // Note the request in our flags. + suspend_request_flags |= + (1u << (static_cast<u32>(ThreadState::SuspendShift) + static_cast<u32>(type))); + + // Try to perform the suspend. + TrySuspend(); +} + +void KThread::Resume(SuspendType type) { + KScopedSchedulerLock sl{kernel}; + + // Clear the request in our flags. + suspend_request_flags &= + ~(1u << (static_cast<u32>(ThreadState::SuspendShift) + static_cast<u32>(type))); + + // Update our state. + const ThreadState old_state = thread_state; + thread_state = static_cast<ThreadState>(GetSuspendFlags() | + static_cast<u32>(old_state & ThreadState::Mask)); + if (thread_state != old_state) { + KScheduler::OnThreadStateChanged(kernel, this, old_state); + } +} + +void KThread::WaitCancel() { + KScopedSchedulerLock sl{kernel}; + + // Check if we're waiting and cancellable. + if (GetState() == ThreadState::Waiting && cancellable) { + if (sleeping_queue != nullptr) { + sleeping_queue->WakeupThread(this); + wait_cancelled = true; + } else { + SetSyncedObject(nullptr, Svc::ResultCancelled); + SetState(ThreadState::Runnable); + wait_cancelled = false; + } + } else { + // Otherwise, note that we cancelled a wait. + wait_cancelled = true; + } +} + +void KThread::TrySuspend() { + ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + ASSERT(IsSuspendRequested()); + + // Ensure that we have no waiters. + if (GetNumKernelWaiters() > 0) { + return; + } + ASSERT(GetNumKernelWaiters() == 0); + + // Perform the suspend. + Suspend(); +} + +void KThread::Suspend() { + ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + ASSERT(IsSuspendRequested()); + + // Set our suspend flags in state. + const auto old_state = thread_state; + thread_state = static_cast<ThreadState>(GetSuspendFlags()) | (old_state & ThreadState::Mask); + + // Note the state change in scheduler. + KScheduler::OnThreadStateChanged(kernel, this, old_state); +} + +void KThread::Continue() { + ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + + // Clear our suspend flags in state. + const auto old_state = thread_state; + thread_state = old_state & ThreadState::Mask; + + // Note the state change in scheduler. + KScheduler::OnThreadStateChanged(kernel, this, old_state); +} + +ResultCode KThread::SetActivity(Svc::ThreadActivity activity) { + // Lock ourselves. + KScopedLightLock lk(activity_pause_lock); + + // Set the activity. + { + // Lock the scheduler. + KScopedSchedulerLock sl{kernel}; + + // Verify our state. + const auto cur_state = GetState(); + R_UNLESS((cur_state == ThreadState::Waiting || cur_state == ThreadState::Runnable), + Svc::ResultInvalidState); + + // Either pause or resume. + if (activity == Svc::ThreadActivity::Paused) { + // Verify that we're not suspended. + R_UNLESS(!IsSuspendRequested(SuspendType::Thread), Svc::ResultInvalidState); + + // Suspend. + RequestSuspend(SuspendType::Thread); + } else { + ASSERT(activity == Svc::ThreadActivity::Runnable); + + // Verify that we're suspended. + R_UNLESS(IsSuspendRequested(SuspendType::Thread), Svc::ResultInvalidState); + + // Resume. + Resume(SuspendType::Thread); + } + } + + // If the thread is now paused, update the pinned waiter list. + if (activity == Svc::ThreadActivity::Paused) { + bool thread_is_pinned{}; + bool thread_is_current{}; + do { + // Lock the scheduler. + KScopedSchedulerLock sl{kernel}; + + // Don't do any further management if our termination has been requested. + R_SUCCEED_IF(IsTerminationRequested()); + + // Check whether the thread is pinned. + if (GetStackParameters().is_pinned) { + // Verify that the current thread isn't terminating. + R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), + Svc::ResultTerminationRequested); + + // Note that the thread was pinned and not current. + thread_is_pinned = true; + thread_is_current = false; + + // Wait until the thread isn't pinned any more. + pinned_waiter_list.push_back(GetCurrentThread(kernel)); + GetCurrentThread(kernel).SetState(ThreadState::Waiting); + } else { + // Check if the thread is currently running. + // If it is, we'll need to retry. + thread_is_current = false; + + for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) { + if (kernel.Scheduler(i).GetCurrentThread() == this) { + thread_is_current = true; + break; + } + } + } + } while (thread_is_current); + + // If the thread was pinned, it no longer is, and we should remove the current thread from + // our waiter list. + if (thread_is_pinned) { + // Lock the scheduler. + KScopedSchedulerLock sl{kernel}; + + // Remove from the list. + pinned_waiter_list.erase(pinned_waiter_list.iterator_to(GetCurrentThread(kernel))); + } + } + + return RESULT_SUCCESS; +} + +ResultCode KThread::GetThreadContext3(std::vector<u8>& out) { + // Lock ourselves. + KScopedLightLock lk{activity_pause_lock}; + + // Get the context. + { + // Lock the scheduler. + KScopedSchedulerLock sl{kernel}; + + // Verify that we're suspended. + R_UNLESS(IsSuspendRequested(SuspendType::Thread), Svc::ResultInvalidState); + + // If we're not terminating, get the thread's user context. + if (!IsTerminationRequested()) { + if (parent->Is64BitProcess()) { + // Mask away mode bits, interrupt bits, IL bit, and other reserved bits. + auto context = GetContext64(); + context.pstate &= 0xFF0FFE20; + + out.resize(sizeof(context)); + std::memcpy(out.data(), &context, sizeof(context)); + } else { + // Mask away mode bits, interrupt bits, IL bit, and other reserved bits. + auto context = GetContext32(); + context.cpsr &= 0xFF0FFE20; + + out.resize(sizeof(context)); + std::memcpy(out.data(), &context, sizeof(context)); + } + } + } + + return RESULT_SUCCESS; +} + +void KThread::AddWaiterImpl(KThread* 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++; + } + + // Keep track of how many kernel waiters we have. + if (Memory::IsKernelAddressKey(thread->GetAddressKey())) { + ASSERT((num_kernel_waiters++) >= 0); + } + + // Insert the waiter. + waiter_list.insert(it, *thread); + thread->SetLockOwner(this); +} + +void KThread::RemoveWaiterImpl(KThread* thread) { + ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + + // Keep track of how many kernel waiters we have. + if (Memory::IsKernelAddressKey(thread->GetAddressKey())) { + ASSERT((num_kernel_waiters--) > 0); + } + + // Remove the waiter. + waiter_list.erase(waiter_list.iterator_to(*thread)); + thread->SetLockOwner(nullptr); +} + +void KThread::RestorePriority(KernelCore& kernel, KThread* 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. + KThread* 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 KThread::AddWaiter(KThread* thread) { + AddWaiterImpl(thread); + RestorePriority(kernel, this); +} + +void KThread::RemoveWaiter(KThread* thread) { + RemoveWaiterImpl(thread); + RestorePriority(kernel, this); +} + +KThread* KThread::RemoveWaiterByKey(s32* out_num_waiters, VAddr key) { + ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + + s32 num_waiters{}; + KThread* next_lock_owner{}; + auto it = waiter_list.begin(); + while (it != waiter_list.end()) { + if (it->GetAddressKey() == key) { + KThread* 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++; + } + } + + // Do priority updates, if we have a next owner. + if (next_lock_owner) { + RestorePriority(kernel, this); + RestorePriority(kernel, next_lock_owner); + } + + // Return output. + *out_num_waiters = num_waiters; + return next_lock_owner; +} + +ResultCode KThread::Run() { + while (true) { + KScopedSchedulerLock lk{kernel}; + + // If either this thread or the current thread are requesting termination, note it. + R_UNLESS(!IsTerminationRequested(), Svc::ResultTerminationRequested); + R_UNLESS(!GetCurrentThread(kernel).IsTerminationRequested(), + Svc::ResultTerminationRequested); + + // Ensure our thread state is correct. + R_UNLESS(GetState() == ThreadState::Initialized, Svc::ResultInvalidState); + + // If the current thread has been asked to suspend, suspend it and retry. + if (GetCurrentThread(kernel).IsSuspended()) { + GetCurrentThread(kernel).Suspend(); + continue; + } + + // If we're not a kernel thread and we've been asked to suspend, suspend ourselves. + if (IsUserThread() && IsSuspended()) { + Suspend(); + } + + // Set our state and finish. + SetState(ThreadState::Runnable); + return RESULT_SUCCESS; + } +} + +void KThread::Exit() { + ASSERT(this == GetCurrentThreadPointer(kernel)); + + // Release the thread resource hint from parent. + if (parent != nullptr) { + // TODO(bunnei): Hint that the resource is about to be released. + resource_limit_release_hint = true; + } + + // Perform termination. + { + KScopedSchedulerLock sl{kernel}; + + // Disallow all suspension. + suspend_allowed_flags = 0; + + // Start termination. + StartTermination(); + } +} + +ResultCode KThread::Sleep(s64 timeout) { + ASSERT(!kernel.GlobalSchedulerContext().IsLocked()); + ASSERT(this == GetCurrentThreadPointer(kernel)); + ASSERT(timeout > 0); + + { + // Setup the scheduling lock and sleep. + KScopedSchedulerLockAndSleep slp{kernel, this, timeout}; + + // Check if the thread should terminate. + if (IsTerminationRequested()) { + slp.CancelSleep(); + return Svc::ResultTerminationRequested; + } + + // Mark the thread as waiting. + SetState(ThreadState::Waiting); + SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Sleep); + } + + // The lock/sleep is done. + + // Cancel the timer. + kernel.TimeManager().UnscheduleTimeEvent(this); + + return RESULT_SUCCESS; +} + +void KThread::SetState(ThreadState state) { + KScopedSchedulerLock sl{kernel}; + + // Clear debugging state + SetMutexWaitAddressForDebugging({}); + SetWaitReasonForDebugging({}); + + const ThreadState old_state = thread_state; + thread_state = + static_cast<ThreadState>((old_state & ~ThreadState::Mask) | (state & ThreadState::Mask)); + if (thread_state != old_state) { + KScheduler::OnThreadStateChanged(kernel, this, old_state); + } +} + +std::shared_ptr<Common::Fiber>& KThread::GetHostContext() { + return host_context; +} + +ResultVal<std::shared_ptr<KThread>> KThread::Create(Core::System& system, ThreadType type_flags, + std::string name, VAddr entry_point, + u32 priority, u64 arg, s32 processor_id, + VAddr stack_top, Process* owner_process) { + std::function<void(void*)> init_func = Core::CpuManager::GetGuestThreadStartFunc(); + void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); + return Create(system, type_flags, name, entry_point, priority, arg, processor_id, stack_top, + owner_process, std::move(init_func), init_func_parameter); +} + +ResultVal<std::shared_ptr<KThread>> KThread::Create(Core::System& system, ThreadType type_flags, + std::string name, VAddr entry_point, + u32 priority, u64 arg, s32 processor_id, + VAddr stack_top, Process* owner_process, + std::function<void(void*)>&& thread_start_func, + void* thread_start_parameter) { + auto& kernel = system.Kernel(); + + std::shared_ptr<KThread> thread = std::make_shared<KThread>(kernel); + + if (const auto result = + thread->InitializeThread(thread.get(), entry_point, arg, stack_top, priority, + processor_id, owner_process, type_flags); + result.IsError()) { + return result; + } + + thread->name = name; + + auto& scheduler = kernel.GlobalSchedulerContext(); + scheduler.AddThread(thread); + + thread->host_context = + std::make_shared<Common::Fiber>(std::move(thread_start_func), thread_start_parameter); + + return MakeResult<std::shared_ptr<KThread>>(std::move(thread)); +} + +KThread* GetCurrentThreadPointer(KernelCore& kernel) { + return kernel.GetCurrentEmuThread(); +} + +KThread& GetCurrentThread(KernelCore& kernel) { + return *GetCurrentThreadPointer(kernel); +} + +s32 GetCurrentCoreId(KernelCore& kernel) { + return GetCurrentThread(kernel).GetCurrentCore(); +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h new file mode 100644 index 000000000..c8ac656a4 --- /dev/null +++ b/src/core/hle/kernel/k_thread.h @@ -0,0 +1,768 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <span> +#include <string> +#include <utility> +#include <vector> + +#include <boost/intrusive/list.hpp> + +#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_light_lock.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/svc_types.h" +#include "core/hle/result.h" + +namespace Common { +class Fiber; +} + +namespace Core { +class ARM_Interface; +class System; +} // namespace Core + +namespace Kernel { + +class GlobalSchedulerContext; +class KernelCore; +class Process; +class KScheduler; +class KThreadQueue; + +using KThreadFunction = VAddr; + +enum class ThreadType : u32 { + Main = 0, + Kernel = 1, + HighPriority = 2, + User = 3, +}; +DECLARE_ENUM_FLAG_OPERATORS(ThreadType); + +enum class SuspendType : u32 { + Process = 0, + Thread = 1, + Debug = 2, + Backtrace = 3, + Init = 4, + + Count, +}; + +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, +}; +DECLARE_ENUM_FLAG_OPERATORS(ThreadState); + +enum class DpcFlag : u32 { + Terminating = (1 << 0), + Terminated = (1 << 1), +}; + +enum class ThreadWaitReasonForDebugging : u32 { + None, ///< Thread is not waiting + Sleep, ///< Thread is waiting due to a SleepThread SVC + IPC, ///< Thread is waiting for the reply from an IPC request + Synchronization, ///< Thread is waiting due to a WaitSynchronization SVC + ConditionVar, ///< Thread is waiting due to a WaitProcessWideKey SVC + Arbitration, ///< Thread is waiting due to a SignalToAddress/WaitForAddress SVC + Suspended, ///< Thread is waiting due to process suspension +}; + +[[nodiscard]] KThread* GetCurrentThreadPointer(KernelCore& kernel); +[[nodiscard]] KThread& GetCurrentThread(KernelCore& kernel); +[[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel); + +class KThread final : public KSynchronizationObject, public boost::intrusive::list_base_hook<> { + friend class KScheduler; + friend class Process; + +public: + static constexpr s32 DefaultThreadPriority = 44; + static constexpr s32 IdleThreadPriority = Svc::LowestThreadPriority + 1; + + explicit KThread(KernelCore& kernel); + ~KThread() override; + +public: + using ThreadContext32 = Core::ARM_Interface::ThreadContext32; + using ThreadContext64 = Core::ARM_Interface::ThreadContext64; + using WaiterList = boost::intrusive::list<KThread>; + + /** + * Creates and returns a new thread. The new thread is immediately scheduled + * @param system The instance of the whole system + * @param name The friendly name desired for the thread + * @param entry_point The address at which the thread should start execution + * @param priority The thread's priority + * @param arg User data to pass to the thread + * @param processor_id The ID(s) of the processors on which the thread is desired to be run + * @param stack_top The address of the thread's stack top + * @param owner_process The parent process for the thread, if null, it's a kernel thread + * @return A shared pointer to the newly created thread + */ + [[nodiscard]] static ResultVal<std::shared_ptr<KThread>> Create( + Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point, + u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process); + + /** + * Creates and returns a new thread. The new thread is immediately scheduled + * @param system The instance of the whole system + * @param name The friendly name desired for the thread + * @param entry_point The address at which the thread should start execution + * @param priority The thread's priority + * @param arg User data to pass to the thread + * @param processor_id The ID(s) of the processors on which the thread is desired to be run + * @param stack_top The address of the thread's stack top + * @param owner_process The parent process for the thread, if null, it's a kernel thread + * @param thread_start_func The function where the host context will start. + * @param thread_start_parameter The parameter which will passed to host context on init + * @return A shared pointer to the newly created thread + */ + [[nodiscard]] static ResultVal<std::shared_ptr<KThread>> Create( + Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point, + u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process, + std::function<void(void*)>&& thread_start_func, void* thread_start_parameter); + + [[nodiscard]] std::string GetName() const override { + return name; + } + + void SetName(std::string new_name) { + name = std::move(new_name); + } + + [[nodiscard]] std::string GetTypeName() const override { + return "Thread"; + } + + static constexpr HandleType HANDLE_TYPE = HandleType::Thread; + [[nodiscard]] HandleType GetHandleType() const override { + return HANDLE_TYPE; + } + + /** + * Gets the thread's current priority + * @return The current thread's priority + */ + [[nodiscard]] s32 GetPriority() const { + return priority; + } + + /** + * Sets the thread's current priority. + * @param priority The new priority. + */ + void SetPriority(s32 value) { + priority = value; + } + + /** + * Gets the thread's nominal priority. + * @return The current thread's nominal priority. + */ + [[nodiscard]] s32 GetBasePriority() const { + return base_priority; + } + + /** + * Gets the thread's thread ID + * @return The thread's ID + */ + [[nodiscard]] u64 GetThreadID() const { + return thread_id; + } + + void ContinueIfHasKernelWaiters() { + if (GetNumKernelWaiters() > 0) { + Continue(); + } + } + + void Wakeup(); + + void SetBasePriority(s32 value); + + [[nodiscard]] ResultCode Run(); + + void Exit(); + + [[nodiscard]] u32 GetSuspendFlags() const { + return suspend_allowed_flags & suspend_request_flags; + } + + [[nodiscard]] bool IsSuspended() const { + return GetSuspendFlags() != 0; + } + + [[nodiscard]] bool IsSuspendRequested(SuspendType type) const { + return (suspend_request_flags & + (1u << (static_cast<u32>(ThreadState::SuspendShift) + static_cast<u32>(type)))) != + 0; + } + + [[nodiscard]] bool IsSuspendRequested() const { + return suspend_request_flags != 0; + } + + void RequestSuspend(SuspendType type); + + void Resume(SuspendType type); + + void TrySuspend(); + + void Continue(); + + void Suspend(); + + void Finalize() override; + + bool IsSignaled() const override; + + void SetSyncedObject(KSynchronizationObject* obj, ResultCode wait_res) { + synced_object = obj; + wait_result = wait_res; + } + + [[nodiscard]] ResultCode GetWaitResult(KSynchronizationObject** out) const { + *out = synced_object; + return wait_result; + } + + /* + * Returns the Thread Local Storage address of the current thread + * @returns VAddr of the thread's TLS + */ + [[nodiscard]] VAddr GetTLSAddress() const { + return tls_address; + } + + /* + * Returns the value of the TPIDR_EL0 Read/Write system register for this thread. + * @returns The value of the TPIDR_EL0 register. + */ + [[nodiscard]] u64 GetTPIDR_EL0() const { + return thread_context_64.tpidr; + } + + /// Sets the value of the TPIDR_EL0 Read/Write system register for this thread. + void SetTPIDR_EL0(u64 value) { + thread_context_64.tpidr = value; + thread_context_32.tpidr = static_cast<u32>(value); + } + + [[nodiscard]] ThreadContext32& GetContext32() { + return thread_context_32; + } + + [[nodiscard]] const ThreadContext32& GetContext32() const { + return thread_context_32; + } + + [[nodiscard]] ThreadContext64& GetContext64() { + return thread_context_64; + } + + [[nodiscard]] const ThreadContext64& GetContext64() const { + return thread_context_64; + } + + [[nodiscard]] std::shared_ptr<Common::Fiber>& GetHostContext(); + + [[nodiscard]] ThreadState GetState() const { + return thread_state & ThreadState::Mask; + } + + [[nodiscard]] ThreadState GetRawState() const { + return thread_state; + } + + void SetState(ThreadState state); + + [[nodiscard]] s64 GetLastScheduledTick() const { + return last_scheduled_tick; + } + + void SetLastScheduledTick(s64 tick) { + last_scheduled_tick = tick; + } + + void AddCpuTime([[maybe_unused]] s32 core_id_, s64 amount) { + cpu_time += amount; + // TODO(bunnei): Debug kernels track per-core tick counts. Should we? + } + + [[nodiscard]] s64 GetCpuTime() const { + return cpu_time; + } + + [[nodiscard]] s32 GetActiveCore() const { + return core_id; + } + + void SetActiveCore(s32 core) { + core_id = core; + } + + [[nodiscard]] s32 GetCurrentCore() const { + return current_core_id; + } + + void SetCurrentCore(s32 core) { + current_core_id = core; + } + + [[nodiscard]] Process* GetOwnerProcess() { + return parent; + } + + [[nodiscard]] const Process* GetOwnerProcess() const { + return parent; + } + + [[nodiscard]] bool IsUserThread() const { + return parent != nullptr; + } + + [[nodiscard]] KThread* GetLockOwner() const { + return lock_owner; + } + + void SetLockOwner(KThread* owner) { + lock_owner = owner; + } + + [[nodiscard]] const KAffinityMask& GetAffinityMask() const { + return physical_affinity_mask; + } + + [[nodiscard]] ResultCode GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask); + + [[nodiscard]] ResultCode GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask); + + [[nodiscard]] ResultCode SetCoreMask(s32 core_id, u64 v_affinity_mask); + + [[nodiscard]] ResultCode SetActivity(Svc::ThreadActivity activity); + + [[nodiscard]] ResultCode Sleep(s64 timeout); + + [[nodiscard]] s64 GetYieldScheduleCount() const { + return schedule_count; + } + + void SetYieldScheduleCount(s64 count) { + schedule_count = count; + } + + void WaitCancel(); + + [[nodiscard]] bool IsWaitCancelled() const { + return wait_cancelled; + } + + [[nodiscard]] void ClearWaitCancelled() { + wait_cancelled = false; + } + + [[nodiscard]] bool IsCancellable() const { + return cancellable; + } + + void SetCancellable() { + cancellable = true; + } + + void ClearCancellable() { + cancellable = false; + } + + [[nodiscard]] bool IsTerminationRequested() const { + return termination_requested || GetRawState() == ThreadState::Terminated; + } + + struct StackParameters { + u8 svc_permission[0x10]; + std::atomic<u8> dpc_flags; + u8 current_svc_id; + bool is_calling_svc; + bool is_in_exception_handler; + bool is_pinned; + s32 disable_count; + KThread* cur_thread; + }; + + [[nodiscard]] StackParameters& GetStackParameters() { + return stack_parameters; + } + + [[nodiscard]] const StackParameters& GetStackParameters() const { + return stack_parameters; + } + + class QueueEntry { + public: + constexpr QueueEntry() = default; + + constexpr void Initialize() { + prev = nullptr; + next = nullptr; + } + + constexpr KThread* GetPrev() const { + return prev; + } + constexpr KThread* GetNext() const { + return next; + } + constexpr void SetPrev(KThread* thread) { + prev = thread; + } + constexpr void SetNext(KThread* thread) { + next = thread; + } + + private: + KThread* prev{}; + KThread* next{}; + }; + + [[nodiscard]] QueueEntry& GetPriorityQueueEntry(s32 core) { + return per_core_priority_queue_entry[core]; + } + + [[nodiscard]] const QueueEntry& GetPriorityQueueEntry(s32 core) const { + return per_core_priority_queue_entry[core]; + } + + void SetSleepingQueue(KThreadQueue* q) { + sleeping_queue = q; + } + + [[nodiscard]] s32 GetDisableDispatchCount() const { + return this->GetStackParameters().disable_count; + } + + void DisableDispatch() { + ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0); + this->GetStackParameters().disable_count++; + } + + void EnableDispatch() { + ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0); + this->GetStackParameters().disable_count--; + } + + void Pin(); + + void Unpin(); + + void SetInExceptionHandler() { + this->GetStackParameters().is_in_exception_handler = true; + } + + void ClearInExceptionHandler() { + this->GetStackParameters().is_in_exception_handler = false; + } + + [[nodiscard]] bool IsInExceptionHandler() const { + return this->GetStackParameters().is_in_exception_handler; + } + + void SetIsCallingSvc() { + this->GetStackParameters().is_calling_svc = true; + } + + void ClearIsCallingSvc() { + this->GetStackParameters().is_calling_svc = false; + } + + [[nodiscard]] bool IsCallingSvc() const { + return this->GetStackParameters().is_calling_svc; + } + + [[nodiscard]] u8 GetSvcId() const { + return this->GetStackParameters().current_svc_id; + } + + void RegisterDpc(DpcFlag flag) { + this->GetStackParameters().dpc_flags |= static_cast<u8>(flag); + } + + void ClearDpc(DpcFlag flag) { + this->GetStackParameters().dpc_flags &= ~static_cast<u8>(flag); + } + + [[nodiscard]] u8 GetDpc() const { + return this->GetStackParameters().dpc_flags; + } + + [[nodiscard]] bool HasDpc() const { + return this->GetDpc() != 0; + } + + void SetWaitReasonForDebugging(ThreadWaitReasonForDebugging reason) { + wait_reason_for_debugging = reason; + } + + [[nodiscard]] ThreadWaitReasonForDebugging GetWaitReasonForDebugging() const { + return wait_reason_for_debugging; + } + + [[nodiscard]] ThreadType GetThreadTypeForDebugging() const { + return thread_type_for_debugging; + } + + void SetWaitObjectsForDebugging(const std::span<KSynchronizationObject*>& objects) { + wait_objects_for_debugging.clear(); + wait_objects_for_debugging.reserve(objects.size()); + for (const auto& object : objects) { + wait_objects_for_debugging.emplace_back(object); + } + } + + [[nodiscard]] const std::vector<KSynchronizationObject*>& 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; + } + + [[nodiscard]] s32 GetIdealCoreForDebugging() const { + return virtual_ideal_core_id; + } + + void AddWaiter(KThread* thread); + + void RemoveWaiter(KThread* thread); + + [[nodiscard]] ResultCode GetThreadContext3(std::vector<u8>& out); + + [[nodiscard]] KThread* RemoveWaiterByKey(s32* out_num_waiters, VAddr key); + + [[nodiscard]] VAddr GetAddressKey() const { + return address_key; + } + + [[nodiscard]] u32 GetAddressKeyValue() const { + return address_key_value; + } + + void SetAddressKey(VAddr key) { + address_key = key; + } + + void SetAddressKey(VAddr key, u32 val) { + address_key = key; + address_key_value = val; + } + + [[nodiscard]] bool HasWaiters() const { + return !waiter_list.empty(); + } + + [[nodiscard]] s32 GetNumKernelWaiters() const { + return num_kernel_waiters; + } + + [[nodiscard]] u64 GetConditionVariableKey() const { + return condvar_key; + } + + [[nodiscard]] u64 GetAddressArbiterKey() const { + return condvar_key; + } + +private: + static constexpr size_t PriorityInheritanceCountMax = 10; + union SyncObjectBuffer { + std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> sync_objects{}; + std::array<Handle, + Svc::ArgumentHandleCountMax*(sizeof(KSynchronizationObject*) / sizeof(Handle))> + handles; + constexpr SyncObjectBuffer() {} + }; + static_assert(sizeof(SyncObjectBuffer::sync_objects) == sizeof(SyncObjectBuffer::handles)); + + 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 <typename T> + requires( + std::same_as<T, KThread> || + std::same_as<T, LightCompareType>) static constexpr int Compare(const T& lhs, + const KThread& rhs) { + const u64 l_key = lhs.GetConditionVariableKey(); + const u64 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; + } + } + }; + + void AddWaiterImpl(KThread* thread); + + void RemoveWaiterImpl(KThread* thread); + + void StartTermination(); + + [[nodiscard]] ResultCode Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top, + s32 prio, s32 virt_core, Process* owner, ThreadType type); + + [[nodiscard]] static ResultCode InitializeThread(KThread* thread, KThreadFunction func, + uintptr_t arg, VAddr user_stack_top, s32 prio, + s32 core, Process* owner, ThreadType type); + + static void RestorePriority(KernelCore& kernel, KThread* thread); + + // For core KThread implementation + ThreadContext32 thread_context_32{}; + ThreadContext64 thread_context_64{}; + Common::IntrusiveRedBlackTreeNode condvar_arbiter_tree_node{}; + s32 priority{}; + using ConditionVariableThreadTreeTraits = + Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert< + &KThread::condvar_arbiter_tree_node>; + using ConditionVariableThreadTree = + ConditionVariableThreadTreeTraits::TreeType<ConditionVariableComparator>; + ConditionVariableThreadTree* condvar_tree{}; + u64 condvar_key{}; + u64 virtual_affinity_mask{}; + KAffinityMask physical_affinity_mask{}; + u64 thread_id{}; + std::atomic<s64> cpu_time{}; + KSynchronizationObject* synced_object{}; + VAddr address_key{}; + Process* parent{}; + VAddr kernel_stack_top{}; + u32* light_ipc_data{}; + VAddr tls_address{}; + KLightLock activity_pause_lock; + s64 schedule_count{}; + s64 last_scheduled_tick{}; + std::array<QueueEntry, Core::Hardware::NUM_CPU_CORES> per_core_priority_queue_entry{}; + KThreadQueue* sleeping_queue{}; + WaiterList waiter_list{}; + WaiterList pinned_waiter_list{}; + KThread* lock_owner{}; + u32 address_key_value{}; + u32 suspend_request_flags{}; + u32 suspend_allowed_flags{}; + ResultCode wait_result{RESULT_SUCCESS}; + s32 base_priority{}; + s32 physical_ideal_core_id{}; + s32 virtual_ideal_core_id{}; + s32 num_kernel_waiters{}; + s32 current_core_id{}; + s32 core_id{}; + KAffinityMask original_physical_affinity_mask{}; + s32 original_physical_ideal_core_id{}; + s32 num_core_migration_disables{}; + ThreadState thread_state{}; + std::atomic<bool> termination_requested{}; + bool wait_cancelled{}; + bool cancellable{}; + bool signaled{}; + bool initialized{}; + bool debug_attached{}; + s8 priority_inheritance_count{}; + bool resource_limit_release_hint{}; + StackParameters stack_parameters{}; + Common::SpinLock context_guard{}; + + // For emulation + std::shared_ptr<Common::Fiber> host_context{}; + + // For debugging + std::vector<KSynchronizationObject*> wait_objects_for_debugging; + VAddr mutex_wait_address_for_debugging{}; + ThreadWaitReasonForDebugging wait_reason_for_debugging{}; + ThreadType thread_type_for_debugging{}; + std::string name; + +public: + using ConditionVariableThreadTreeType = ConditionVariableThreadTree; + + void SetConditionVariable(ConditionVariableThreadTree* tree, VAddr address, u64 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, u64 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; + } +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/k_thread_queue.h b/src/core/hle/kernel/k_thread_queue.h new file mode 100644 index 000000000..c52eba249 --- /dev/null +++ b/src/core/hle/kernel/k_thread_queue.h @@ -0,0 +1,81 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/kernel/k_thread.h" + +namespace Kernel { + +class KThreadQueue { +public: + explicit KThreadQueue(KernelCore& kernel) : kernel{kernel} {} + + bool IsEmpty() const { + return wait_list.empty(); + } + + KThread::WaiterList::iterator begin() { + return wait_list.begin(); + } + KThread::WaiterList::iterator end() { + return wait_list.end(); + } + + bool SleepThread(KThread* t) { + KScopedSchedulerLock sl{kernel}; + + // If the thread needs terminating, don't enqueue it. + if (t->IsTerminationRequested()) { + return false; + } + + // Set the thread's queue and mark it as waiting. + t->SetSleepingQueue(this); + t->SetState(ThreadState::Waiting); + + // Add the thread to the queue. + wait_list.push_back(*t); + + return true; + } + + void WakeupThread(KThread* t) { + KScopedSchedulerLock sl{kernel}; + + // Remove the thread from the queue. + wait_list.erase(wait_list.iterator_to(*t)); + + // Mark the thread as no longer sleeping. + t->SetState(ThreadState::Runnable); + t->SetSleepingQueue(nullptr); + } + + KThread* WakeupFrontThread() { + KScopedSchedulerLock sl{kernel}; + + if (wait_list.empty()) { + return nullptr; + } else { + // Remove the thread from the queue. + auto it = wait_list.begin(); + KThread* thread = std::addressof(*it); + wait_list.erase(it); + + ASSERT(thread->GetState() == ThreadState::Waiting); + + // Mark the thread as no longer sleeping. + thread->SetState(ThreadState::Runnable); + thread->SetSleepingQueue(nullptr); + + return thread; + } + } + +private: + KernelCore& kernel; + KThread::WaiterList wait_list{}; +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index c0ff287a6..df309d523 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -29,6 +29,7 @@ #include "core/hle/kernel/errors.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/k_scheduler.h" +#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/memory/memory_layout.h" #include "core/hle/kernel/memory/memory_manager.h" @@ -38,7 +39,6 @@ #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/thread.h" #include "core/hle/kernel/time_manager.h" #include "core/hle/lock.h" #include "core/hle/result.h" @@ -57,11 +57,13 @@ struct KernelCore::Impl { } void Initialize(KernelCore& kernel) { + global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel); + RegisterHostThread(); - global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel); service_thread_manager = std::make_unique<Common::ThreadWorker>(1, "yuzu:ServiceThreadManager"); + is_phantom_mode_for_singlecore = false; InitializePhysicalCores(); InitializeSystemResourceLimit(kernel); @@ -116,14 +118,14 @@ struct KernelCore::Impl { void InitializePhysicalCores() { exclusive_monitor = Core::MakeExclusiveMonitor(system.Memory(), Core::Hardware::NUM_CPU_CORES); - for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { + for (u32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { schedulers[i] = std::make_unique<Kernel::KScheduler>(system, i); cores.emplace_back(i, system, *schedulers[i], interrupts); } } void InitializeSchedulers() { - for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { + for (u32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { cores[i].Scheduler().Initialize(); } } @@ -168,11 +170,9 @@ struct KernelCore::Impl { std::string name = "Suspend Thread Id:" + std::to_string(i); std::function<void(void*)> init_func = Core::CpuManager::GetSuspendThreadStartFunc(); void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); - const auto type = - static_cast<ThreadType>(THREADTYPE_KERNEL | THREADTYPE_HLE | THREADTYPE_SUSPEND); - auto thread_res = - Thread::Create(system, type, std::move(name), 0, 0, 0, static_cast<u32>(i), 0, - nullptr, std::move(init_func), init_func_parameter); + auto thread_res = KThread::Create(system, ThreadType::HighPriority, std::move(name), 0, + 0, 0, static_cast<u32>(i), 0, nullptr, + std::move(init_func), init_func_parameter); suspend_threads[i] = std::move(thread_res).Unwrap(); } @@ -207,6 +207,17 @@ struct KernelCore::Impl { return host_thread_id; } + // Gets the dummy KThread for the caller, allocating a new one if this is the first time + KThread* GetHostDummyThread() { + const thread_local auto thread = + KThread::Create( + system, ThreadType::Main, fmt::format("DummyThread:{}", GetHostThreadId()), 0, + KThread::DefaultThreadPriority, 0, static_cast<u32>(3), 0, nullptr, + []([[maybe_unused]] void* arg) { UNREACHABLE(); }, nullptr) + .Unwrap(); + return thread.get(); + } + /// Registers a CPU core thread by allocating a host thread ID for it void RegisterCoreThread(std::size_t core_id) { ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); @@ -219,6 +230,7 @@ struct KernelCore::Impl { /// Registers a new host thread by allocating a host thread ID for it void RegisterHostThread() { [[maybe_unused]] const auto this_id = GetHostThreadId(); + [[maybe_unused]] const auto dummy_thread = GetHostDummyThread(); } [[nodiscard]] u32 GetCurrentHostThreadID() { @@ -229,20 +241,21 @@ struct KernelCore::Impl { return this_id; } - [[nodiscard]] Core::EmuThreadHandle GetCurrentEmuThreadID() { - Core::EmuThreadHandle result = Core::EmuThreadHandle::InvalidHandle(); - result.host_handle = GetCurrentHostThreadID(); - if (result.host_handle >= Core::Hardware::NUM_CPU_CORES) { - return result; - } - const Kernel::KScheduler& sched = cores[result.host_handle].Scheduler(); - const Kernel::Thread* current = sched.GetCurrentThread(); - if (current != nullptr && !current->IsPhantomMode()) { - result.guest_handle = current->GetGlobalHandle(); - } else { - result.guest_handle = InvalidHandle; + bool IsPhantomModeForSingleCore() const { + return is_phantom_mode_for_singlecore; + } + + void SetIsPhantomModeForSingleCore(bool value) { + ASSERT(!is_multicore); + is_phantom_mode_for_singlecore = value; + } + + KThread* GetCurrentEmuThread() { + const auto thread_id = GetCurrentHostThreadID(); + if (thread_id >= Core::Hardware::NUM_CPU_CORES) { + return GetHostDummyThread(); } - return result; + return schedulers[thread_id]->GetCurrentThread(); } void InitializeMemoryLayout() { @@ -342,11 +355,12 @@ struct KernelCore::Impl { // the release of itself std::unique_ptr<Common::ThreadWorker> service_thread_manager; - std::array<std::shared_ptr<Thread>, Core::Hardware::NUM_CPU_CORES> suspend_threads{}; + std::array<std::shared_ptr<KThread>, Core::Hardware::NUM_CPU_CORES> suspend_threads{}; std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{}; std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{}; bool is_multicore{}; + bool is_phantom_mode_for_singlecore{}; u32 single_core_thread_id{}; std::array<u64, Core::Hardware::NUM_CPU_CORES> svc_ticks{}; @@ -380,8 +394,8 @@ std::shared_ptr<ResourceLimit> KernelCore::GetSystemResourceLimit() const { return impl->system_resource_limit; } -std::shared_ptr<Thread> KernelCore::RetrieveThreadFromGlobalHandleTable(Handle handle) const { - return impl->global_handle_table.Get<Thread>(handle); +std::shared_ptr<KThread> KernelCore::RetrieveThreadFromGlobalHandleTable(Handle handle) const { + return impl->global_handle_table.Get<KThread>(handle); } void KernelCore::AppendNewProcess(std::shared_ptr<Process> process) { @@ -546,8 +560,8 @@ u32 KernelCore::GetCurrentHostThreadID() const { return impl->GetCurrentHostThreadID(); } -Core::EmuThreadHandle KernelCore::GetCurrentEmuThreadID() const { - return impl->GetCurrentEmuThreadID(); +KThread* KernelCore::GetCurrentEmuThread() const { + return impl->GetCurrentEmuThread(); } Memory::MemoryManager& KernelCore::MemoryManager() { @@ -645,4 +659,12 @@ void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> servi }); } +bool KernelCore::IsPhantomModeForSingleCore() const { + return impl->IsPhantomModeForSingleCore(); +} + +void KernelCore::SetIsPhantomModeForSingleCore(bool value) { + impl->SetIsPhantomModeForSingleCore(value); +} + } // namespace Kernel diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 933d9a7d6..e7c77727b 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -43,9 +43,13 @@ class KScheduler; class SharedMemory; class ServiceThread; class Synchronization; -class Thread; +class KThread; class TimeManager; +using EmuThreadHandle = uintptr_t; +constexpr EmuThreadHandle EmuThreadHandleInvalid{}; +constexpr EmuThreadHandle EmuThreadHandleReserved{1ULL << 63}; + /// Represents a single instance of the kernel. class KernelCore { private: @@ -84,7 +88,7 @@ public: std::shared_ptr<ResourceLimit> GetSystemResourceLimit() const; /// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table. - std::shared_ptr<Thread> RetrieveThreadFromGlobalHandleTable(Handle handle) const; + std::shared_ptr<KThread> RetrieveThreadFromGlobalHandleTable(Handle handle) const; /// Adds the given shared pointer to an internal list of active processes. void AppendNewProcess(std::shared_ptr<Process> process); @@ -161,8 +165,8 @@ public: /// Determines whether or not the given port is a valid named port. bool IsValidNamedPort(NamedPortTable::const_iterator port) const; - /// Gets the current host_thread/guest_thread handle. - Core::EmuThreadHandle GetCurrentEmuThreadID() const; + /// Gets the current host_thread/guest_thread pointer. + KThread* GetCurrentEmuThread() const; /// Gets the current host_thread handle. u32 GetCurrentHostThreadID() const; @@ -237,10 +241,14 @@ public: */ void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread); + /// Workaround for single-core mode when preempting threads while idle. + bool IsPhantomModeForSingleCore() const; + void SetIsPhantomModeForSingleCore(bool value); + private: friend class Object; friend class Process; - friend class Thread; + friend class KThread; /// Creates a new object ID, incrementing the internal object ID counter. u32 CreateNewObjectID(); diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h index 27124ef67..be7fcb5fb 100644 --- a/src/core/hle/kernel/object.h +++ b/src/core/hle/kernel/object.h @@ -61,6 +61,8 @@ public: */ bool IsWaitable() const; + virtual void Finalize() = 0; + protected: /// The kernel instance this object was created under. KernelCore& kernel; diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 37b77fa6e..0edbfc4cc 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -16,13 +16,13 @@ #include "core/hle/kernel/code_set.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/k_scheduler.h" +#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/memory/memory_block_manager.h" #include "core/hle/kernel/memory/page_table.h" #include "core/hle/kernel/memory/slab_heap.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" -#include "core/hle/kernel/thread.h" #include "core/hle/lock.h" #include "core/memory.h" #include "core/settings.h" @@ -38,11 +38,10 @@ namespace { */ void SetupMainThread(Core::System& system, Process& owner_process, u32 priority, VAddr stack_top) { const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart(); - ThreadType type = THREADTYPE_USER; - auto thread_res = Thread::Create(system, type, "main", entry_point, priority, 0, - owner_process.GetIdealCore(), stack_top, &owner_process); + auto thread_res = KThread::Create(system, ThreadType::User, "main", entry_point, priority, 0, + owner_process.GetIdealCoreId(), stack_top, &owner_process); - std::shared_ptr<Thread> thread = std::move(thread_res).Unwrap(); + std::shared_ptr<KThread> thread = std::move(thread_res).Unwrap(); // Register 1 must be a handle to the main thread const Handle thread_handle = owner_process.GetHandleTable().Create(thread).Unwrap(); @@ -137,6 +136,23 @@ std::shared_ptr<ResourceLimit> Process::GetResourceLimit() const { return resource_limit; } +void Process::IncrementThreadCount() { + ASSERT(num_threads >= 0); + num_created_threads++; + + if (const auto count = ++num_threads; count > peak_num_threads) { + peak_num_threads = count; + } +} + +void Process::DecrementThreadCount() { + ASSERT(num_threads > 0); + + if (const auto count = --num_threads; count == 0) { + UNIMPLEMENTED_MSG("Process termination is not implemented!"); + } +} + u64 Process::GetTotalPhysicalMemoryAvailable() const { const u64 capacity{resource_limit->GetCurrentResourceValue(ResourceType::PhysicalMemory) + page_table->GetTotalHeapSize() + GetSystemResourceSize() + image_size + @@ -162,11 +178,66 @@ u64 Process::GetTotalPhysicalMemoryUsedWithoutSystemResource() const { return GetTotalPhysicalMemoryUsed() - GetSystemResourceUsage(); } -void Process::RegisterThread(const Thread* thread) { +bool Process::ReleaseUserException(KThread* thread) { + KScopedSchedulerLock sl{kernel}; + + if (exception_thread == thread) { + exception_thread = nullptr; + + // Remove waiter thread. + s32 num_waiters{}; + KThread* next = thread->RemoveWaiterByKey( + std::addressof(num_waiters), + reinterpret_cast<uintptr_t>(std::addressof(exception_thread))); + if (next != nullptr) { + if (next->GetState() == ThreadState::Waiting) { + next->SetState(ThreadState::Runnable); + } else { + KScheduler::SetSchedulerUpdateNeeded(kernel); + } + } + + return true; + } else { + return false; + } +} + +void Process::PinCurrentThread() { + ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + + // Get the current thread. + const s32 core_id = GetCurrentCoreId(kernel); + KThread* cur_thread = GetCurrentThreadPointer(kernel); + + // Pin it. + PinThread(core_id, cur_thread); + cur_thread->Pin(); + + // An update is needed. + KScheduler::SetSchedulerUpdateNeeded(kernel); +} + +void Process::UnpinCurrentThread() { + ASSERT(kernel.GlobalSchedulerContext().IsLocked()); + + // Get the current thread. + const s32 core_id = GetCurrentCoreId(kernel); + KThread* cur_thread = GetCurrentThreadPointer(kernel); + + // Unpin it. + cur_thread->Unpin(); + UnpinThread(core_id, cur_thread); + + // An update is needed. + KScheduler::SetSchedulerUpdateNeeded(kernel); +} + +void Process::RegisterThread(const KThread* thread) { thread_list.push_back(thread); } -void Process::UnregisterThread(const Thread* thread) { +void Process::UnregisterThread(const KThread* thread) { thread_list.remove(thread); } @@ -267,7 +338,7 @@ void Process::Run(s32 main_thread_priority, u64 stack_size) { void Process::PrepareForTermination() { ChangeStatus(ProcessStatus::Exiting); - const auto stop_threads = [this](const std::vector<std::shared_ptr<Thread>>& thread_list) { + const auto stop_threads = [this](const std::vector<std::shared_ptr<KThread>>& thread_list) { for (auto& thread : thread_list) { if (thread->GetOwnerProcess() != this) continue; @@ -279,7 +350,7 @@ void Process::PrepareForTermination() { ASSERT_MSG(thread->GetState() == ThreadState::Waiting, "Exiting processes with non-waiting threads is currently unimplemented"); - thread->Stop(); + thread->Exit(); } }; @@ -372,7 +443,7 @@ bool Process::IsSignaled() const { Process::Process(Core::System& system) : KSynchronizationObject{system.Kernel()}, page_table{std::make_unique<Memory::PageTable>(system)}, handle_table{system.Kernel()}, - address_arbiter{system}, condition_var{system}, system{system} {} + address_arbiter{system}, condition_var{system}, state_lock{system.Kernel()}, system{system} {} Process::~Process() = default; diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 564e1f27d..26e647743 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -30,7 +30,7 @@ namespace Kernel { class KernelCore; class ResourceLimit; -class Thread; +class KThread; class TLSPage; struct CodeSet; @@ -173,10 +173,15 @@ public: std::shared_ptr<ResourceLimit> GetResourceLimit() const; /// Gets the ideal CPU core ID for this process - u8 GetIdealCore() const { + u8 GetIdealCoreId() const { return ideal_core; } + /// Checks if the specified thread priority is valid. + bool CheckThreadPriority(s32 prio) const { + return ((1ULL << prio) & GetPriorityMask()) != 0; + } + /// Gets the bitmask of allowed cores that this process' threads can run on. u64 GetCoreMask() const { return capabilities.GetCoreMask(); @@ -212,6 +217,14 @@ public: return is_64bit_process; } + [[nodiscard]] bool IsSuspended() const { + return is_suspended; + } + + void SetSuspended(bool suspended) { + is_suspended = suspended; + } + /// Gets the total running time of the process instance in ticks. u64 GetCPUTimeTicks() const { return total_process_running_time_ticks; @@ -232,6 +245,33 @@ public: ++schedule_count; } + void IncrementThreadCount(); + void DecrementThreadCount(); + + void SetRunningThread(s32 core, KThread* thread, u64 idle_count) { + running_threads[core] = thread; + running_thread_idle_counts[core] = idle_count; + } + + void ClearRunningThread(KThread* thread) { + for (size_t i = 0; i < running_threads.size(); ++i) { + if (running_threads[i] == thread) { + running_threads[i] = nullptr; + } + } + } + + [[nodiscard]] KThread* GetRunningThread(s32 core) const { + return running_threads[core]; + } + + bool ReleaseUserException(KThread* thread); + + [[nodiscard]] KThread* GetPinnedThread(s32 core_id) const { + ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES)); + return pinned_threads[core_id]; + } + /// Gets 8 bytes of random data for svcGetInfo RandomEntropy u64 GetRandomEntropy(std::size_t index) const { return random_entropy.at(index); @@ -252,17 +292,17 @@ public: u64 GetTotalPhysicalMemoryUsedWithoutSystemResource() const; /// Gets the list of all threads created with this process as their owner. - const std::list<const Thread*>& GetThreadList() const { + const std::list<const KThread*>& GetThreadList() const { return thread_list; } /// Registers a thread as being created under this process, /// adding it to this process' thread list. - void RegisterThread(const Thread* thread); + void RegisterThread(const KThread* thread); /// Unregisters a thread from this process, removing it /// from this process' thread list. - void UnregisterThread(const Thread* thread); + void UnregisterThread(const KThread* thread); /// Clears the signaled state of the process if and only if it's signaled. /// @@ -303,6 +343,15 @@ public: bool IsSignaled() const override; + void Finalize() override {} + + void PinCurrentThread(); + void UnpinCurrentThread(); + + KLightLock& GetStateLock() { + return state_lock; + } + /////////////////////////////////////////////////////////////////////////////////////////////// // Thread-local storage management @@ -313,6 +362,20 @@ public: void FreeTLSRegion(VAddr tls_address); private: + void PinThread(s32 core_id, KThread* thread) { + ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES)); + ASSERT(thread != nullptr); + ASSERT(pinned_threads[core_id] == nullptr); + pinned_threads[core_id] = thread; + } + + void UnpinThread(s32 core_id, KThread* thread) { + ASSERT(0 <= core_id && core_id < static_cast<s32>(Core::Hardware::NUM_CPU_CORES)); + ASSERT(thread != nullptr); + ASSERT(pinned_threads[core_id] == thread); + pinned_threads[core_id] = nullptr; + } + /// Changes the process status. If the status is different /// from the current process status, then this will trigger /// a process signal. @@ -380,7 +443,7 @@ private: std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy{}; /// List of threads that are running with this process as their owner. - std::list<const Thread*> thread_list; + std::list<const KThread*> thread_list; /// Address of the top of the main thread's stack VAddr main_thread_stack_top{}; @@ -401,6 +464,19 @@ private: s64 schedule_count{}; bool is_signaled{}; + bool is_suspended{}; + + std::atomic<s32> num_created_threads{}; + std::atomic<u16> num_threads{}; + u16 peak_num_threads{}; + + std::array<KThread*, Core::Hardware::NUM_CPU_CORES> running_threads{}; + std::array<u64, Core::Hardware::NUM_CPU_CORES> running_thread_idle_counts{}; + std::array<KThread*, Core::Hardware::NUM_CPU_CORES> pinned_threads{}; + + KThread* exception_thread{}; + + KLightLock state_lock; /// 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..596d01479 100644 --- a/src/core/hle/kernel/readable_event.cpp +++ b/src/core/hle/kernel/readable_event.cpp @@ -7,10 +7,10 @@ #include "common/logging/log.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/k_scheduler.h" +#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/object.h" #include "core/hle/kernel/readable_event.h" -#include "core/hle/kernel/thread.h" namespace Kernel { diff --git a/src/core/hle/kernel/readable_event.h b/src/core/hle/kernel/readable_event.h index 34e477274..2195710c2 100644 --- a/src/core/hle/kernel/readable_event.h +++ b/src/core/hle/kernel/readable_event.h @@ -47,6 +47,8 @@ public: bool IsSignaled() const override; + void Finalize() override {} + private: explicit ReadableEvent(KernelCore& kernel); diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h index 936cc4d0f..464d4f2a6 100644 --- a/src/core/hle/kernel/resource_limit.h +++ b/src/core/hle/kernel/resource_limit.h @@ -85,6 +85,8 @@ public: */ ResultCode SetLimitValue(ResourceType resource, s64 value); + void Finalize() override {} + private: // TODO(Subv): Increment resource limit current values in their respective Kernel::T::Create // functions diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp index 82857f93b..fe7a483c4 100644 --- a/src/core/hle/kernel/server_port.cpp +++ b/src/core/hle/kernel/server_port.cpp @@ -6,10 +6,10 @@ #include "common/assert.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/errors.h" +#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/object.h" #include "core/hle/kernel/server_port.h" #include "core/hle/kernel/server_session.h" -#include "core/hle/kernel/thread.h" namespace Kernel { diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h index 6470df993..29b4f2509 100644 --- a/src/core/hle/kernel/server_port.h +++ b/src/core/hle/kernel/server_port.h @@ -81,6 +81,8 @@ public: bool IsSignaled() const override; + void Finalize() override {} + private: /// ServerSessions waiting to be accepted by the port std::vector<std::shared_ptr<ServerSession>> pending_sessions; diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 4f2bb7822..790dbb998 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -15,11 +15,11 @@ #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/k_scheduler.h" +#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/server_session.h" #include "core/hle/kernel/session.h" -#include "core/hle/kernel/thread.h" #include "core/memory.h" namespace Kernel { @@ -116,7 +116,7 @@ ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& con return RESULT_SUCCESS; } -ResultCode ServerSession::QueueSyncRequest(std::shared_ptr<Thread> thread, +ResultCode ServerSession::QueueSyncRequest(std::shared_ptr<KThread> thread, Core::Memory::Memory& memory) { u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(thread->GetTLSAddress()))}; auto context = @@ -154,14 +154,14 @@ ResultCode ServerSession::CompleteSyncRequest(HLERequestContext& context) { KScopedSchedulerLock lock(kernel); if (!context.IsThreadWaiting()) { context.GetThread().Wakeup(); - context.GetThread().SetSynchronizationResults(nullptr, result); + context.GetThread().SetSyncedObject(nullptr, result); } } return result; } -ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread, +ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<KThread> thread, Core::Memory::Memory& memory, Core::Timing::CoreTiming& core_timing) { return QueueSyncRequest(std::move(thread), memory); diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index 9155cf7f5..c42d5ee59 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h @@ -29,7 +29,7 @@ class HLERequestContext; class KernelCore; class Session; class SessionRequestHandler; -class Thread; +class KThread; /** * Kernel object representing the server endpoint of an IPC session. Sessions are the basic CTR-OS @@ -95,7 +95,7 @@ public: * * @returns ResultCode from the operation. */ - ResultCode HandleSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory, + ResultCode HandleSyncRequest(std::shared_ptr<KThread> thread, Core::Memory::Memory& memory, Core::Timing::CoreTiming& core_timing); /// Called when a client disconnection occurs. @@ -126,9 +126,11 @@ public: bool IsSignaled() const override; + void Finalize() override {} + private: /// Queues a sync request from the emulated application. - ResultCode QueueSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory); + ResultCode QueueSyncRequest(std::shared_ptr<KThread> thread, Core::Memory::Memory& memory); /// Completes a sync request from the emulated application. ResultCode CompleteSyncRequest(HLERequestContext& context); @@ -149,12 +151,12 @@ private: /// List of threads that are pending a response after a sync request. This list is processed in /// a LIFO manner, thus, the last request will be dispatched first. /// TODO(Subv): Verify if this is indeed processed in LIFO using a hardware test. - std::vector<std::shared_ptr<Thread>> pending_requesting_threads; + std::vector<std::shared_ptr<KThread>> pending_requesting_threads; /// Thread whose request is currently being handled. A request is considered "handled" when a /// response is sent via svcReplyAndReceive. /// TODO(Subv): Find a better name for this. - std::shared_ptr<Thread> currently_handling; + std::shared_ptr<KThread> currently_handling; /// When set to True, converts the session to a domain at the end of the command bool convert_to_domain{}; diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h index f6dd2c1d2..fa3c5651a 100644 --- a/src/core/hle/kernel/session.h +++ b/src/core/hle/kernel/session.h @@ -39,6 +39,8 @@ public: bool IsSignaled() const override; + void Finalize() override {} + std::shared_ptr<ClientSession> Client() { if (auto result{client.lock()}) { return result; diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index 0ef87235c..623bd8b11 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h @@ -71,6 +71,8 @@ public: return device_memory.GetPointer(physical_address + offset); } + void Finalize() override {} + private: Core::DeviceMemory& device_memory; Process* owner_process{}; diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index cc8b661af..7fd514e9d 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -29,6 +29,7 @@ #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/k_thread.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/memory/memory_block.h" #include "core/hle/kernel/memory/memory_layout.h" @@ -42,7 +43,6 @@ #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/thread.h" #include "core/hle/kernel/time_manager.h" #include "core/hle/kernel/transfer_memory.h" #include "core/hle/kernel/writable_event.h" @@ -351,7 +351,8 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) { session->SendSyncRequest(SharedFrom(thread), system.Memory(), system.CoreTiming()); } - return thread->GetSignalingResult(); + KSynchronizationObject* dummy{}; + return thread->GetWaitResult(std::addressof(dummy)); } static ResultCode SendSyncRequest32(Core::System& system, Handle handle) { @@ -359,27 +360,26 @@ static ResultCode SendSyncRequest32(Core::System& system, Handle handle) { } /// Get the ID for the specified thread. -static ResultCode GetThreadId(Core::System& system, u64* thread_id, Handle thread_handle) { +static ResultCode GetThreadId(Core::System& system, u64* out_thread_id, Handle thread_handle) { LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle); + // Get the thread from its handle. const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); - const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle); - if (!thread) { - LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", thread_handle); - return ERR_INVALID_HANDLE; - } + const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle); + R_UNLESS(thread, Svc::ResultInvalidHandle); - *thread_id = thread->GetThreadID(); + // Get the thread's id. + *out_thread_id = thread->GetThreadID(); return RESULT_SUCCESS; } -static ResultCode GetThreadId32(Core::System& system, u32* thread_id_low, u32* thread_id_high, - Handle thread_handle) { - u64 thread_id{}; - const ResultCode result{GetThreadId(system, &thread_id, thread_handle)}; +static ResultCode GetThreadId32(Core::System& system, u32* out_thread_id_low, + u32* out_thread_id_high, Handle thread_handle) { + u64 out_thread_id{}; + const ResultCode result{GetThreadId(system, &out_thread_id, thread_handle)}; - *thread_id_low = static_cast<u32>(thread_id >> 32); - *thread_id_high = static_cast<u32>(thread_id & std::numeric_limits<u32>::max()); + *out_thread_id_low = static_cast<u32>(out_thread_id >> 32); + *out_thread_id_high = static_cast<u32>(out_thread_id & std::numeric_limits<u32>::max()); return result; } @@ -395,7 +395,7 @@ static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle han return RESULT_SUCCESS; } - const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(handle); + const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(handle); if (thread) { const Process* const owner_process = thread->GetOwnerProcess(); if (!owner_process) { @@ -473,15 +473,13 @@ static ResultCode WaitSynchronization32(Core::System& system, u32 timeout_low, u static ResultCode CancelSynchronization(Core::System& system, Handle thread_handle) { LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle); + // Get the thread from its handle. const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); - std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle); - if (!thread) { - LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", - thread_handle); - return ERR_INVALID_HANDLE; - } + std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle); + R_UNLESS(thread, Svc::ResultInvalidHandle); - thread->CancelWait(); + // Cancel the thread's wait. + thread->WaitCancel(); return RESULT_SUCCESS; } @@ -630,7 +628,7 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) { handle_debug_buffer(info1, info2); auto* const current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread(); - const auto thread_processor_id = current_thread->GetProcessorID(); + const auto thread_processor_id = current_thread->GetActiveCore(); system.ArmInterface(static_cast<std::size_t>(thread_processor_id)).LogBacktrace(); } } @@ -872,7 +870,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha return ERR_INVALID_COMBINATION; } - const auto thread = system.Kernel().CurrentProcess()->GetHandleTable().Get<Thread>( + const auto thread = system.Kernel().CurrentProcess()->GetHandleTable().Get<KThread>( static_cast<Handle>(handle)); if (!thread) { LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", @@ -888,7 +886,7 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTicks(); u64 out_ticks = 0; if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) { - const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks(); + const u64 thread_ticks = current_thread->GetCpuTime(); out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks); } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) { @@ -1025,129 +1023,109 @@ static ResultCode UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size return UnmapPhysicalMemory(system, addr, size); } -/// Sets the thread activity -static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 activity) { - LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity); - if (activity > static_cast<u32>(ThreadActivity::Paused)) { - return ERR_INVALID_ENUM_VALUE; +constexpr bool IsValidThreadActivity(Svc::ThreadActivity thread_activity) { + switch (thread_activity) { + case Svc::ThreadActivity::Runnable: + case Svc::ThreadActivity::Paused: + return true; + default: + return false; } +} - const auto* current_process = system.Kernel().CurrentProcess(); - const std::shared_ptr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); - if (!thread) { - LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); - return ERR_INVALID_HANDLE; - } +/// Sets the thread activity +static ResultCode SetThreadActivity(Core::System& system, Handle thread_handle, + Svc::ThreadActivity thread_activity) { + LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", thread_handle, + thread_activity); - if (thread->GetOwnerProcess() != current_process) { - LOG_ERROR(Kernel_SVC, - "The current process does not own the current thread, thread_handle={:08X} " - "thread_pid={}, " - "current_process_pid={}", - handle, thread->GetOwnerProcess()->GetProcessID(), - current_process->GetProcessID()); - return ERR_INVALID_HANDLE; - } + // Validate the activity. + R_UNLESS(IsValidThreadActivity(thread_activity), Svc::ResultInvalidEnumValue); - if (thread.get() == system.Kernel().CurrentScheduler()->GetCurrentThread()) { - LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread"); - return ERR_BUSY; - } + // Get the thread from its handle. + auto& kernel = system.Kernel(); + const auto& handle_table = kernel.CurrentProcess()->GetHandleTable(); + const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle); + R_UNLESS(thread, Svc::ResultInvalidHandle); + + // Check that the activity is being set on a non-current thread for the current process. + R_UNLESS(thread->GetOwnerProcess() == kernel.CurrentProcess(), Svc::ResultInvalidHandle); + R_UNLESS(thread.get() != GetCurrentThreadPointer(kernel), Svc::ResultBusy); + + // Set the activity. + R_TRY(thread->SetActivity(thread_activity)); - return thread->SetActivity(static_cast<ThreadActivity>(activity)); + return RESULT_SUCCESS; } -static ResultCode SetThreadActivity32(Core::System& system, Handle handle, u32 activity) { - return SetThreadActivity(system, handle, activity); +static ResultCode SetThreadActivity32(Core::System& system, Handle thread_handle, + Svc::ThreadActivity thread_activity) { + return SetThreadActivity(system, thread_handle, thread_activity); } /// Gets the thread context -static ResultCode GetThreadContext(Core::System& system, VAddr thread_context, Handle handle) { - LOG_DEBUG(Kernel_SVC, "called, context=0x{:08X}, thread=0x{:X}", thread_context, handle); +static ResultCode GetThreadContext(Core::System& system, VAddr out_context, Handle thread_handle) { + LOG_DEBUG(Kernel_SVC, "called, out_context=0x{:08X}, thread_handle=0x{:X}", out_context, + thread_handle); + // Get the thread from its handle. const auto* current_process = system.Kernel().CurrentProcess(); - const std::shared_ptr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); - if (!thread) { - LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); - return ERR_INVALID_HANDLE; - } + const std::shared_ptr<KThread> thread = + current_process->GetHandleTable().Get<KThread>(thread_handle); + R_UNLESS(thread, Svc::ResultInvalidHandle); - if (thread->GetOwnerProcess() != current_process) { - LOG_ERROR(Kernel_SVC, - "The current process does not own the current thread, thread_handle={:08X} " - "thread_pid={}, " - "current_process_pid={}", - handle, thread->GetOwnerProcess()->GetProcessID(), - current_process->GetProcessID()); - return ERR_INVALID_HANDLE; - } + // Require the handle be to a non-current thread in the current process. + R_UNLESS(thread->GetOwnerProcess() == current_process, Svc::ResultInvalidHandle); + R_UNLESS(thread.get() != system.Kernel().CurrentScheduler()->GetCurrentThread(), + Svc::ResultBusy); - if (thread.get() == system.Kernel().CurrentScheduler()->GetCurrentThread()) { - LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread"); - return ERR_BUSY; - } - - Core::ARM_Interface::ThreadContext64 ctx = thread->GetContext64(); - // Mask away mode bits, interrupt bits, IL bit, and other reserved bits. - ctx.pstate &= 0xFF0FFE20; + // Get the thread context. + std::vector<u8> context; + R_TRY(thread->GetThreadContext3(context)); - // If 64-bit, we can just write the context registers directly and we're good. - // However, if 32-bit, we have to ensure some registers are zeroed out. - if (!current_process->Is64BitProcess()) { - std::fill(ctx.cpu_registers.begin() + 15, ctx.cpu_registers.end(), 0); - std::fill(ctx.vector_registers.begin() + 16, ctx.vector_registers.end(), u128{}); - } + // Copy the thread context to user space. + system.Memory().WriteBlock(out_context, context.data(), context.size()); - system.Memory().WriteBlock(thread_context, &ctx, sizeof(ctx)); return RESULT_SUCCESS; } -static ResultCode GetThreadContext32(Core::System& system, u32 thread_context, Handle handle) { - return GetThreadContext(system, thread_context, handle); +static ResultCode GetThreadContext32(Core::System& system, u32 out_context, Handle thread_handle) { + return GetThreadContext(system, out_context, thread_handle); } /// Gets the priority for the specified thread -static ResultCode GetThreadPriority(Core::System& system, u32* priority, Handle handle) { +static ResultCode GetThreadPriority(Core::System& system, u32* out_priority, Handle handle) { LOG_TRACE(Kernel_SVC, "called"); + // Get the thread from its handle. const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); - const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(handle); - if (!thread) { - *priority = 0; - LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); - return ERR_INVALID_HANDLE; - } + const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(handle); + R_UNLESS(thread, Svc::ResultInvalidHandle); - *priority = thread->GetPriority(); + // Get the thread's priority. + *out_priority = thread->GetPriority(); return RESULT_SUCCESS; } -static ResultCode GetThreadPriority32(Core::System& system, u32* priority, Handle handle) { - return GetThreadPriority(system, priority, handle); +static ResultCode GetThreadPriority32(Core::System& system, u32* out_priority, Handle handle) { + return GetThreadPriority(system, out_priority, handle); } /// Sets the priority for the specified thread static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 priority) { LOG_TRACE(Kernel_SVC, "called"); - if (priority > THREADPRIO_LOWEST) { - LOG_ERROR( - Kernel_SVC, - "An invalid priority was specified, expected {} but got {} for thread_handle={:08X}", - THREADPRIO_LOWEST, priority, handle); - return ERR_INVALID_THREAD_PRIORITY; - } + // Validate the priority. + R_UNLESS(Svc::HighestThreadPriority <= priority && priority <= Svc::LowestThreadPriority, + Svc::ResultInvalidPriority); - const auto* const current_process = system.Kernel().CurrentProcess(); - - std::shared_ptr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); - if (!thread) { - LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); - return ERR_INVALID_HANDLE; - } + // Get the thread from its handle. + const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); + const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(handle); + R_UNLESS(thread, Svc::ResultInvalidHandle); + // Set the thread priority. thread->SetBasePriority(priority); - return RESULT_SUCCESS; } @@ -1438,62 +1416,50 @@ static void ExitProcess(Core::System& system) { current_process->PrepareForTermination(); // Kill the current thread - system.Kernel().CurrentScheduler()->GetCurrentThread()->Stop(); + system.Kernel().CurrentScheduler()->GetCurrentThread()->Exit(); } static void ExitProcess32(Core::System& system) { ExitProcess(system); } +static constexpr bool IsValidCoreId(int32_t core_id) { + return (0 <= core_id && core_id < static_cast<int32_t>(Core::Hardware::NUM_CPU_CORES)); +} + /// Creates a new thread static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg, - VAddr stack_top, u32 priority, s32 processor_id) { + VAddr stack_bottom, u32 priority, s32 core_id) { LOG_DEBUG(Kernel_SVC, - "called entrypoint=0x{:08X}, arg=0x{:08X}, stacktop=0x{:08X}, " - "threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}", - entry_point, arg, stack_top, priority, processor_id, *out_handle); + "called entry_point=0x{:08X}, arg=0x{:08X}, stack_bottom=0x{:08X}, " + "priority=0x{:08X}, core_id=0x{:08X}", + entry_point, arg, stack_bottom, priority, core_id); - auto* const current_process = system.Kernel().CurrentProcess(); - - if (processor_id == THREADPROCESSORID_IDEAL) { - // Set the target CPU to the one specified by the process. - processor_id = current_process->GetIdealCore(); - ASSERT(processor_id != THREADPROCESSORID_IDEAL); + // Adjust core id, if it's the default magic. + auto& kernel = system.Kernel(); + auto& process = *kernel.CurrentProcess(); + if (core_id == Svc::IdealCoreUseProcessValue) { + core_id = process.GetIdealCoreId(); } - if (processor_id < THREADPROCESSORID_0 || processor_id > THREADPROCESSORID_3) { - LOG_ERROR(Kernel_SVC, "Invalid thread processor ID: {}", processor_id); - return ERR_INVALID_PROCESSOR_ID; - } + // Validate arguments. + R_UNLESS(IsValidCoreId(core_id), Svc::ResultInvalidCoreId); + R_UNLESS(((1ULL << core_id) & process.GetCoreMask()) != 0, Svc::ResultInvalidCoreId); - const u64 core_mask = current_process->GetCoreMask(); - if ((core_mask | (1ULL << processor_id)) != core_mask) { - LOG_ERROR(Kernel_SVC, "Invalid thread core specified ({})", processor_id); - return ERR_INVALID_PROCESSOR_ID; - } + R_UNLESS(Svc::HighestThreadPriority <= priority && priority <= Svc::LowestThreadPriority, + Svc::ResultInvalidPriority); + R_UNLESS(process.CheckThreadPriority(priority), Svc::ResultInvalidPriority); - if (priority > THREADPRIO_LOWEST) { - LOG_ERROR(Kernel_SVC, - "Invalid thread priority specified ({}). Must be within the range 0-64", - priority); - return ERR_INVALID_THREAD_PRIORITY; - } + ASSERT(process.GetResourceLimit()->Reserve(ResourceType::Threads, 1)); - if (((1ULL << priority) & current_process->GetPriorityMask()) == 0) { - LOG_ERROR(Kernel_SVC, "Invalid thread priority specified ({})", priority); - return ERR_INVALID_THREAD_PRIORITY; + std::shared_ptr<KThread> thread; + { + KScopedLightLock lk{process.GetStateLock()}; + CASCADE_RESULT(thread, KThread::Create(system, ThreadType::User, "", entry_point, priority, + arg, core_id, stack_bottom, &process)); } - auto& kernel = system.Kernel(); - - ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(ResourceType::Threads, 1)); - - ThreadType type = THREADTYPE_USER; - CASCADE_RESULT(std::shared_ptr<Thread> thread, - Thread::Create(system, type, "", entry_point, priority, arg, processor_id, - stack_top, current_process)); - - const auto new_thread_handle = current_process->GetHandleTable().Create(thread); + const auto new_thread_handle = process.GetHandleTable().Create(thread); if (new_thread_handle.Failed()) { LOG_ERROR(Kernel_SVC, "Failed to create handle with error=0x{:X}", new_thread_handle.Code().raw); @@ -1517,17 +1483,15 @@ static ResultCode CreateThread32(Core::System& system, Handle* out_handle, u32 p static ResultCode StartThread(Core::System& system, Handle thread_handle) { LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", thread_handle); + // Get the thread from its handle. const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); - const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle); - if (!thread) { - LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", - thread_handle); - return ERR_INVALID_HANDLE; - } + const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle); + R_UNLESS(thread, Svc::ResultInvalidHandle); - ASSERT(thread->GetState() == ThreadState::Initialized); + // Try to start the thread. + R_TRY(thread->Run()); - return thread->Start(); + return RESULT_SUCCESS; } static ResultCode StartThread32(Core::System& system, Handle thread_handle) { @@ -1540,7 +1504,7 @@ static void ExitThread(Core::System& system) { auto* const current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread(); system.GlobalSchedulerContext().RemoveThread(SharedFrom(current_thread)); - current_thread->Stop(); + current_thread->Exit(); } static void ExitThread32(Core::System& system) { @@ -1549,34 +1513,28 @@ static void ExitThread32(Core::System& system) { /// Sleep the current thread static void SleepThread(Core::System& system, s64 nanoseconds) { - LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds); + auto& kernel = system.Kernel(); + const auto yield_type = static_cast<Svc::YieldType>(nanoseconds); - enum class SleepType : s64 { - YieldWithoutCoreMigration = 0, - YieldWithCoreMigration = -1, - YieldAndWaitForLoadBalancing = -2, - }; + LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds); - auto& scheduler = *system.Kernel().CurrentScheduler(); - if (nanoseconds <= 0) { - switch (static_cast<SleepType>(nanoseconds)) { - case SleepType::YieldWithoutCoreMigration: { - scheduler.YieldWithoutCoreMigration(); - break; - } - case SleepType::YieldWithCoreMigration: { - scheduler.YieldWithCoreMigration(); - break; - } - case SleepType::YieldAndWaitForLoadBalancing: { - scheduler.YieldToAnyThread(); - break; - } - default: - UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds); - } + // When the input tick is positive, sleep. + if (nanoseconds > 0) { + // Convert the timeout from nanoseconds to ticks. + // NOTE: Nintendo does not use this conversion logic in WaitSynchronization... + + // Sleep. + // NOTE: Nintendo does not check the result of this sleep. + static_cast<void>(GetCurrentThread(kernel).Sleep(nanoseconds)); + } else if (yield_type == Svc::YieldType::WithoutCoreMigration) { + KScheduler::YieldWithoutCoreMigration(kernel); + } else if (yield_type == Svc::YieldType::WithCoreMigration) { + KScheduler::YieldWithCoreMigration(kernel); + } else if (yield_type == Svc::YieldType::ToAnyThread) { + KScheduler::YieldToAnyThread(kernel); } else { - scheduler.GetCurrentThread()->Sleep(nanoseconds); + // Nintendo does nothing at all if an otherwise invalid value is passed. + UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds); } } @@ -1839,95 +1797,72 @@ static ResultCode CreateTransferMemory32(Core::System& system, Handle* handle, u return CreateTransferMemory(system, handle, addr, size, permissions); } -static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, u32* core, - u64* mask) { +static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, s32* out_core_id, + u64* out_affinity_mask) { LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); + // Get the thread from its handle. const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); - const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle); - if (!thread) { - LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", - thread_handle); - *core = 0; - *mask = 0; - return ERR_INVALID_HANDLE; - } + const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle); + R_UNLESS(thread, Svc::ResultInvalidHandle); - *core = thread->GetIdealCore(); - *mask = thread->GetAffinityMask().GetAffinityMask(); + // Get the core mask. + R_TRY(thread->GetCoreMask(out_core_id, out_affinity_mask)); return RESULT_SUCCESS; } -static ResultCode GetThreadCoreMask32(Core::System& system, Handle thread_handle, u32* core, - u32* mask_low, u32* mask_high) { - u64 mask{}; - const auto result = GetThreadCoreMask(system, thread_handle, core, &mask); - *mask_high = static_cast<u32>(mask >> 32); - *mask_low = static_cast<u32>(mask); +static ResultCode GetThreadCoreMask32(Core::System& system, Handle thread_handle, s32* out_core_id, + u32* out_affinity_mask_low, u32* out_affinity_mask_high) { + u64 out_affinity_mask{}; + const auto result = GetThreadCoreMask(system, thread_handle, out_core_id, &out_affinity_mask); + *out_affinity_mask_high = static_cast<u32>(out_affinity_mask >> 32); + *out_affinity_mask_low = static_cast<u32>(out_affinity_mask); return result; } -static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, u32 core, +static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle, s32 core_id, u64 affinity_mask) { - LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, core=0x{:X}, affinity_mask=0x{:016X}", - thread_handle, core, affinity_mask); - - const auto* const current_process = system.Kernel().CurrentProcess(); + LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, core_id=0x{:X}, affinity_mask=0x{:016X}", + thread_handle, core_id, affinity_mask); - if (core == static_cast<u32>(THREADPROCESSORID_IDEAL)) { - const u8 ideal_cpu_core = current_process->GetIdealCore(); + const auto& current_process = *system.Kernel().CurrentProcess(); - ASSERT(ideal_cpu_core != static_cast<u8>(THREADPROCESSORID_IDEAL)); - - // Set the target CPU to the ideal core specified by the process. - core = ideal_cpu_core; - affinity_mask = 1ULL << core; + // Determine the core id/affinity mask. + if (core_id == Svc::IdealCoreUseProcessValue) { + core_id = current_process.GetIdealCoreId(); + affinity_mask = (1ULL << core_id); } else { - const u64 core_mask = current_process->GetCoreMask(); - - if ((core_mask | affinity_mask) != core_mask) { - LOG_ERROR( - Kernel_SVC, - "Invalid processor ID specified (core_mask=0x{:08X}, affinity_mask=0x{:016X})", - core_mask, affinity_mask); - return ERR_INVALID_PROCESSOR_ID; - } - - if (affinity_mask == 0) { - LOG_ERROR(Kernel_SVC, "Specfified affinity mask is zero."); - return ERR_INVALID_COMBINATION; - } - - if (core < Core::Hardware::NUM_CPU_CORES) { - if ((affinity_mask & (1ULL << core)) == 0) { - LOG_ERROR(Kernel_SVC, - "Core is not enabled for the current mask, core={}, mask={:016X}", core, - affinity_mask); - return ERR_INVALID_COMBINATION; - } - } else if (core != static_cast<u32>(THREADPROCESSORID_DONT_CARE) && - core != static_cast<u32>(THREADPROCESSORID_DONT_UPDATE)) { - LOG_ERROR(Kernel_SVC, "Invalid processor ID specified (core={}).", core); - return ERR_INVALID_PROCESSOR_ID; + // Validate the affinity mask. + const u64 process_core_mask = current_process.GetCoreMask(); + R_UNLESS((affinity_mask | process_core_mask) == process_core_mask, + Svc::ResultInvalidCoreId); + R_UNLESS(affinity_mask != 0, Svc::ResultInvalidCombination); + + // Validate the core id. + if (IsValidCoreId(core_id)) { + R_UNLESS(((1ULL << core_id) & affinity_mask) != 0, Svc::ResultInvalidCombination); + } else { + R_UNLESS(core_id == Svc::IdealCoreNoUpdate || core_id == Svc::IdealCoreDontCare, + Svc::ResultInvalidCoreId); } } - const auto& handle_table = current_process->GetHandleTable(); - const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle); - if (!thread) { - LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", - thread_handle); - return ERR_INVALID_HANDLE; - } + // Get the thread from its handle. + const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); + const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle); + R_UNLESS(thread, Svc::ResultInvalidHandle); - return thread->SetCoreAndAffinityMask(core, affinity_mask); + // Set the core mask. + R_TRY(thread->SetCoreMask(core_id, affinity_mask)); + + return RESULT_SUCCESS; } -static ResultCode SetThreadCoreMask32(Core::System& system, Handle thread_handle, u32 core, +static ResultCode SetThreadCoreMask32(Core::System& system, Handle thread_handle, s32 core_id, u32 affinity_mask_low, u32 affinity_mask_high) { const auto affinity_mask = u64{affinity_mask_low} | (u64{affinity_mask_high} << 32); - return SetThreadCoreMask(system, thread_handle, core, affinity_mask); + return SetThreadCoreMask(system, thread_handle, core_id, affinity_mask); } static ResultCode CreateEvent(Core::System& system, Handle* write_handle, Handle* read_handle) { @@ -2491,7 +2426,7 @@ void Call(Core::System& system, u32 immediate) { kernel.EnterSVCProfile(); auto* thread = kernel.CurrentScheduler()->GetCurrentThread(); - thread->SetContinuousOnSVC(true); + thread->SetIsCallingSvc(); const FunctionDef* info = system.CurrentProcess()->Is64BitProcess() ? GetSVCInfo64(immediate) : GetSVCInfo32(immediate); @@ -2507,7 +2442,7 @@ void Call(Core::System& system, u32 immediate) { kernel.ExitSVCProfile(); - if (!thread->IsContinuousOnSVC()) { + if (!thread->IsCallingSvc()) { auto* host_context = thread->GetHostContext().get(); host_context->Rewind(); } diff --git a/src/core/hle/kernel/svc_results.h b/src/core/hle/kernel/svc_results.h index 78282f021..7b897fbce 100644 --- a/src/core/hle/kernel/svc_results.h +++ b/src/core/hle/kernel/svc_results.h @@ -8,13 +8,18 @@ namespace Kernel::Svc { +constexpr ResultCode ResultNoSynchronizationObject{ErrorModule::Kernel, 57}; constexpr ResultCode ResultTerminationRequested{ErrorModule::Kernel, 59}; constexpr ResultCode ResultInvalidAddress{ErrorModule::Kernel, 102}; constexpr ResultCode ResultInvalidCurrentMemory{ErrorModule::Kernel, 106}; +constexpr ResultCode ResultInvalidPriority{ErrorModule::Kernel, 112}; +constexpr ResultCode ResultInvalidCoreId{ErrorModule::Kernel, 113}; constexpr ResultCode ResultInvalidHandle{ErrorModule::Kernel, 114}; +constexpr ResultCode ResultInvalidCombination{ErrorModule::Kernel, 116}; constexpr ResultCode ResultTimedOut{ErrorModule::Kernel, 117}; constexpr ResultCode ResultCancelled{ErrorModule::Kernel, 118}; constexpr ResultCode ResultInvalidEnumValue{ErrorModule::Kernel, 120}; +constexpr ResultCode ResultBusy{ErrorModule::Kernel, 122}; constexpr ResultCode ResultInvalidState{ErrorModule::Kernel, 125}; } // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc_types.h b/src/core/hle/kernel/svc_types.h index d623f7a50..ec463b97c 100644 --- a/src/core/hle/kernel/svc_types.h +++ b/src/core/hle/kernel/svc_types.h @@ -77,4 +77,22 @@ enum class ArbitrationType : u32 { WaitIfEqual = 2, }; +enum class YieldType : s64 { + WithoutCoreMigration = 0, + WithCoreMigration = -1, + ToAnyThread = -2, +}; + +enum class ThreadActivity : u32 { + Runnable = 0, + Paused = 1, +}; + +constexpr inline s32 IdealCoreDontCare = -1; +constexpr inline s32 IdealCoreUseProcessValue = -2; +constexpr inline s32 IdealCoreNoUpdate = -3; + +constexpr inline s32 LowestThreadPriority = 63; +constexpr inline s32 HighestThreadPriority = 0; + } // namespace Kernel::Svc diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index a32750ed7..96afd544b 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h @@ -58,6 +58,14 @@ void SvcWrap64(Core::System& system) { func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw); } +// Used by SetThreadActivity +template <ResultCode func(Core::System&, Handle, Svc::ThreadActivity)> +void SvcWrap64(Core::System& system) { + FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), + static_cast<Svc::ThreadActivity>(Param(system, 1))) + .raw); +} + template <ResultCode func(Core::System&, u32, u64, u64, u64)> void SvcWrap64(Core::System& system) { FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), @@ -158,9 +166,18 @@ void SvcWrap64(Core::System& system) { .raw); } -template <ResultCode func(Core::System&, u32, u32*, u64*)> +// Used by SetThreadCoreMask +template <ResultCode func(Core::System&, Handle, s32, u64)> void SvcWrap64(Core::System& system) { - u32 param_1 = 0; + FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), + static_cast<s32>(Param(system, 1)), Param(system, 2)) + .raw); +} + +// Used by GetThreadCoreMask +template <ResultCode func(Core::System&, Handle, s32*, u64*)> +void SvcWrap64(Core::System& system) { + s32 param_1 = 0; u64 param_2 = 0; const ResultCode retval = func(system, static_cast<u32>(Param(system, 2)), ¶m_1, ¶m_2); @@ -473,12 +490,35 @@ void SvcWrap32(Core::System& system) { FuncReturn(system, retval); } +// Used by GetThreadCoreMask32 +template <ResultCode func(Core::System&, Handle, s32*, u32*, u32*)> +void SvcWrap32(Core::System& system) { + s32 param_1 = 0; + u32 param_2 = 0; + u32 param_3 = 0; + + const u32 retval = func(system, Param32(system, 2), ¶m_1, ¶m_2, ¶m_3).raw; + system.CurrentArmInterface().SetReg(1, param_1); + system.CurrentArmInterface().SetReg(2, param_2); + system.CurrentArmInterface().SetReg(3, param_3); + FuncReturn(system, retval); +} + // Used by SignalProcessWideKey32 template <void func(Core::System&, u32, s32)> void SvcWrap32(Core::System& system) { func(system, static_cast<u32>(Param(system, 0)), static_cast<s32>(Param(system, 1))); } +// Used by SetThreadActivity32 +template <ResultCode func(Core::System&, Handle, Svc::ThreadActivity)> +void SvcWrap32(Core::System& system) { + const u32 retval = func(system, static_cast<Handle>(Param(system, 0)), + static_cast<Svc::ThreadActivity>(Param(system, 1))) + .raw; + FuncReturn(system, retval); +} + // Used by SetThreadPriority32 template <ResultCode func(Core::System&, Handle, u32)> void SvcWrap32(Core::System& system) { @@ -487,7 +527,7 @@ void SvcWrap32(Core::System& system) { FuncReturn(system, retval); } -// Used by SetThreadCoreMask32 +// Used by SetMemoryAttribute32 template <ResultCode func(Core::System&, Handle, u32, u32, u32)> void SvcWrap32(Core::System& system) { const u32 retval = @@ -497,6 +537,16 @@ void SvcWrap32(Core::System& system) { FuncReturn(system, retval); } +// Used by SetThreadCoreMask32 +template <ResultCode func(Core::System&, Handle, s32, u32, u32)> +void SvcWrap32(Core::System& system) { + const u32 retval = + func(system, static_cast<Handle>(Param(system, 0)), static_cast<s32>(Param(system, 1)), + static_cast<u32>(Param(system, 2)), static_cast<u32>(Param(system, 3))) + .raw; + FuncReturn(system, retval); +} + // Used by WaitProcessWideKeyAtomic32 template <ResultCode func(Core::System&, u32, u32, Handle, u32, u32)> void SvcWrap32(Core::System& system) { diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp deleted file mode 100644 index d97323255..000000000 --- a/src/core/hle/kernel/thread.cpp +++ /dev/null @@ -1,460 +0,0 @@ -// Copyright 2014 Citra Emulator Project / PPSSPP Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <algorithm> -#include <cinttypes> -#include <optional> -#include <vector> - -#include "common/assert.h" -#include "common/common_types.h" -#include "common/fiber.h" -#include "common/logging/log.h" -#include "common/thread_queue_list.h" -#include "core/core.h" -#include "core/cpu_manager.h" -#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" -#include "core/hle/kernel/time_manager.h" -#include "core/hle/result.h" -#include "core/memory.h" - -#ifdef ARCHITECTURE_x86_64 -#include "core/arm/dynarmic/arm_dynarmic_32.h" -#include "core/arm/dynarmic/arm_dynarmic_64.h" -#endif - -namespace Kernel { - -bool Thread::IsSignaled() const { - return signaled; -} - -Thread::Thread(KernelCore& kernel) : KSynchronizationObject{kernel} {} -Thread::~Thread() = default; - -void Thread::Stop() { - { - KScopedSchedulerLock lock(kernel); - SetState(ThreadState::Terminated); - signaled = true; - NotifyAvailable(); - kernel.GlobalHandleTable().Close(global_handle); - - if (owner_process) { - owner_process->UnregisterThread(this); - - // Mark the TLS slot in the thread's page as free. - owner_process->FreeTLSRegion(tls_address); - } - has_exited = true; - } - global_handle = 0; -} - -void Thread::Wakeup() { - KScopedSchedulerLock lock(kernel); - SetState(ThreadState::Runnable); -} - -ResultCode Thread::Start() { - KScopedSchedulerLock lock(kernel); - SetState(ThreadState::Runnable); - return RESULT_SUCCESS; -} - -void Thread::CancelWait() { - KScopedSchedulerLock lock(kernel); - if (GetState() != ThreadState::Waiting || !is_cancellable) { - is_sync_cancelled = true; - return; - } - // TODO(Blinkhawk): Implement cancel of server session - is_sync_cancelled = false; - SetSynchronizationResults(nullptr, ERR_SYNCHRONIZATION_CANCELED); - SetState(ThreadState::Runnable); -} - -static void ResetThreadContext32(Core::ARM_Interface::ThreadContext32& context, u32 stack_top, - u32 entry_point, u32 arg) { - context = {}; - context.cpu_registers[0] = arg; - context.cpu_registers[15] = entry_point; - context.cpu_registers[13] = stack_top; -} - -static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context, VAddr stack_top, - VAddr entry_point, u64 arg) { - context = {}; - context.cpu_registers[0] = arg; - context.pc = entry_point; - context.sp = stack_top; - // TODO(merry): Perform a hardware test to determine the below value. - context.fpcr = 0; -} - -std::shared_ptr<Common::Fiber>& Thread::GetHostContext() { - return host_context; -} - -ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadType type_flags, - std::string name, VAddr entry_point, u32 priority, - u64 arg, s32 processor_id, VAddr stack_top, - Process* owner_process) { - std::function<void(void*)> init_func = Core::CpuManager::GetGuestThreadStartFunc(); - void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); - return Create(system, type_flags, name, entry_point, priority, arg, processor_id, stack_top, - owner_process, std::move(init_func), init_func_parameter); -} - -ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadType type_flags, - std::string name, VAddr entry_point, u32 priority, - u64 arg, s32 processor_id, VAddr stack_top, - Process* owner_process, - std::function<void(void*)>&& thread_start_func, - void* thread_start_parameter) { - auto& kernel = system.Kernel(); - // Check if priority is in ranged. Lowest priority -> highest priority id. - if (priority > THREADPRIO_LOWEST && ((type_flags & THREADTYPE_IDLE) == 0)) { - LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority); - return ERR_INVALID_THREAD_PRIORITY; - } - - if (processor_id > THREADPROCESSORID_MAX) { - LOG_ERROR(Kernel_SVC, "Invalid processor id: {}", processor_id); - return ERR_INVALID_PROCESSOR_ID; - } - - if (owner_process) { - if (!system.Memory().IsValidVirtualAddress(*owner_process, entry_point)) { - LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point); - // TODO (bunnei): Find the correct error code to use here - return RESULT_UNKNOWN; - } - } - - std::shared_ptr<Thread> thread = std::make_shared<Thread>(kernel); - - thread->thread_id = kernel.CreateNewThreadID(); - thread->thread_state = ThreadState::Initialized; - 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->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->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); - } - if (owner_process) { - thread->tls_address = thread->owner_process->CreateTLSRegion(); - thread->owner_process->RegisterThread(thread.get()); - } else { - thread->tls_address = 0; - } - - // TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used - // to initialize the context - if ((type_flags & THREADTYPE_HLE) == 0) { - ResetThreadContext32(thread->context_32, static_cast<u32>(stack_top), - static_cast<u32>(entry_point), static_cast<u32>(arg)); - ResetThreadContext64(thread->context_64, stack_top, entry_point, arg); - } - thread->host_context = - std::make_shared<Common::Fiber>(std::move(thread_start_func), thread_start_parameter); - - return MakeResult<std::shared_ptr<Thread>>(std::move(thread)); -} - -void Thread::SetBasePriority(u32 priority) { - 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); -} - -void Thread::SetSynchronizationResults(KSynchronizationObject* object, ResultCode result) { - signaling_object = object; - signaling_result = result; -} - -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); - - // Clear debugging state - SetMutexWaitAddressForDebugging({}); - SetWaitReasonForDebugging({}); - - const ThreadState old_state = thread_state; - thread_state = - static_cast<ThreadState>((old_state & ~ThreadState::Mask) | (state & ThreadState::Mask)); - if (thread_state != old_state) { - KScheduler::OnThreadStateChanged(kernel, this, old_state); - } -} - -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++; - } - - // Keep track of how many kernel waiters we have. - if (Memory::IsKernelAddressKey(thread->GetAddressKey())) { - ASSERT((num_kernel_waiters++) >= 0); - } - - // Insert the waiter. - waiter_list.insert(it, *thread); - thread->SetLockOwner(this); -} - -void Thread::RemoveWaiterImpl(Thread* thread) { - ASSERT(kernel.GlobalSchedulerContext().IsLocked()); - - // Keep track of how many kernel waiters we have. - if (Memory::IsKernelAddressKey(thread->GetAddressKey())) { - ASSERT((num_kernel_waiters--) > 0); - } - - // Remove the waiter. - waiter_list.erase(waiter_list.iterator_to(*thread)); - thread->SetLockOwner(nullptr); -} - -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++; - } - } - - // Do priority updates, if we have a next owner. - if (next_lock_owner) { - RestorePriority(kernel, this); - RestorePriority(kernel, next_lock_owner); - } - - // Return output. - *out_num_waiters = num_waiters; - return next_lock_owner; -} - -ResultCode Thread::SetActivity(ThreadActivity value) { - KScopedSchedulerLock lock(kernel); - - auto sched_status = GetState(); - - if (sched_status != ThreadState::Runnable && sched_status != ThreadState::Waiting) { - return ERR_INVALID_STATE; - } - - if (IsTerminationRequested()) { - return RESULT_SUCCESS; - } - - if (value == ThreadActivity::Paused) { - if ((pausing_state & static_cast<u32>(ThreadSchedFlags::ThreadPauseFlag)) != 0) { - return ERR_INVALID_STATE; - } - AddSchedulingFlag(ThreadSchedFlags::ThreadPauseFlag); - } else { - if ((pausing_state & static_cast<u32>(ThreadSchedFlags::ThreadPauseFlag)) == 0) { - return ERR_INVALID_STATE; - } - RemoveSchedulingFlag(ThreadSchedFlags::ThreadPauseFlag); - } - return RESULT_SUCCESS; -} - -ResultCode Thread::Sleep(s64 nanoseconds) { - Handle event_handle{}; - { - KScopedSchedulerLockAndSleep lock(kernel, event_handle, this, nanoseconds); - SetState(ThreadState::Waiting); - SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Sleep); - } - - if (event_handle != InvalidHandle) { - auto& time_manager = kernel.TimeManager(); - time_manager.UnscheduleTimeEvent(event_handle); - } - return RESULT_SUCCESS; -} - -void Thread::AddSchedulingFlag(ThreadSchedFlags flag) { - const auto old_state = GetRawState(); - pausing_state |= static_cast<u32>(flag); - const auto base_scheduling = GetState(); - thread_state = base_scheduling | static_cast<ThreadState>(pausing_state); - KScheduler::OnThreadStateChanged(kernel, this, old_state); -} - -void Thread::RemoveSchedulingFlag(ThreadSchedFlags flag) { - const auto old_state = GetRawState(); - pausing_state &= ~static_cast<u32>(flag); - const auto base_scheduling = GetState(); - thread_state = base_scheduling | static_cast<ThreadState>(pausing_state); - KScheduler::OnThreadStateChanged(kernel, this, old_state); -} - -ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) { - KScopedSchedulerLock lock(kernel); - const auto HighestSetCore = [](u64 mask, u32 max_cores) { - for (s32 core = static_cast<s32>(max_cores - 1); core >= 0; core--) { - if (((mask >> core) & 1) != 0) { - return core; - } - } - return -1; - }; - - const bool use_override = affinity_override_count != 0; - if (new_core == THREADPROCESSORID_DONT_UPDATE) { - new_core = use_override ? ideal_core_override : ideal_core; - if ((new_affinity_mask & (1ULL << new_core)) == 0) { - LOG_ERROR(Kernel, "New affinity mask is incorrect! new_core={}, new_affinity_mask={}", - new_core, new_affinity_mask); - return ERR_INVALID_COMBINATION; - } - } - if (use_override) { - ideal_core_override = new_core; - } else { - const auto old_affinity_mask = affinity_mask; - affinity_mask.SetAffinityMask(new_affinity_mask); - ideal_core = new_core; - if (old_affinity_mask.GetAffinityMask() != new_affinity_mask) { - const s32 old_core = processor_id; - if (processor_id >= 0 && !affinity_mask.GetAffinity(processor_id)) { - if (static_cast<s32>(ideal_core) < 0) { - processor_id = HighestSetCore(affinity_mask.GetAffinityMask(), - Core::Hardware::NUM_CPU_CORES); - } else { - processor_id = ideal_core; - } - } - KScheduler::OnThreadAffinityMaskChanged(kernel, this, old_affinity_mask, old_core); - } - } - return RESULT_SUCCESS; -} - -} // namespace Kernel diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h deleted file mode 100644 index 6b66c9a0e..000000000 --- a/src/core/hle/kernel/thread.h +++ /dev/null @@ -1,782 +0,0 @@ -// Copyright 2014 Citra Emulator Project / PPSSPP Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <array> -#include <functional> -#include <span> -#include <string> -#include <utility> -#include <vector> - -#include <boost/intrusive/list.hpp> - -#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/result.h" - -namespace Common { -class Fiber; -} - -namespace Core { -class ARM_Interface; -class System; -} // namespace Core - -namespace Kernel { - -class GlobalSchedulerContext; -class KernelCore; -class Process; -class KScheduler; - -enum ThreadPriority : u32 { - THREADPRIO_HIGHEST = 0, ///< Highest thread priority - THREADPRIO_MAX_CORE_MIGRATION = 2, ///< Highest priority for a core migration - THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps - THREADPRIO_DEFAULT = 44, ///< Default thread priority for userland apps - THREADPRIO_LOWEST = 63, ///< Lowest thread priority - THREADPRIO_COUNT = 64, ///< Total number of possible thread priorities. -}; - -enum ThreadType : u32 { - THREADTYPE_USER = 0x1, - THREADTYPE_KERNEL = 0x2, - THREADTYPE_HLE = 0x4, - THREADTYPE_IDLE = 0x8, - THREADTYPE_SUSPEND = 0x10, -}; - -enum ThreadProcessorId : s32 { - /// Indicates that no particular processor core is preferred. - THREADPROCESSORID_DONT_CARE = -1, - - /// Run thread on the ideal core specified by the process. - THREADPROCESSORID_IDEAL = -2, - - /// Indicates that the preferred processor ID shouldn't be updated in - /// a core mask setting operation. - THREADPROCESSORID_DONT_UPDATE = -3, - - THREADPROCESSORID_0 = 0, ///< Run thread on core 0 - THREADPROCESSORID_1 = 1, ///< Run thread on core 1 - THREADPROCESSORID_2 = 2, ///< Run thread on core 2 - THREADPROCESSORID_3 = 3, ///< Run thread on core 3 - THREADPROCESSORID_MAX = 4, ///< Processor ID must be less than this - - /// Allowed CPU mask - THREADPROCESSORID_DEFAULT_MASK = (1 << THREADPROCESSORID_0) | (1 << THREADPROCESSORID_1) | - (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, -}; -DECLARE_ENUM_FLAG_OPERATORS(ThreadState); - -enum class ThreadWakeupReason { - Signal, // The thread was woken up by WakeupAllWaitingThreads due to an object signal. - Timeout // The thread was woken up due to a wait timeout. -}; - -enum class ThreadActivity : u32 { - Normal = 0, - Paused = 1, -}; - -enum class ThreadSchedFlags : u32 { - ProcessPauseFlag = 1 << 4, - ThreadPauseFlag = 1 << 5, - ProcessDebugPauseFlag = 1 << 6, - KernelInitPauseFlag = 1 << 8, -}; - -enum class ThreadWaitReasonForDebugging : u32 { - None, ///< Thread is not waiting - Sleep, ///< Thread is waiting due to a SleepThread SVC - IPC, ///< Thread is waiting for the reply from an IPC request - Synchronization, ///< Thread is waiting due to a WaitSynchronization SVC - ConditionVar, ///< Thread is waiting due to a WaitProcessWideKey SVC - Arbitration, ///< Thread is waiting due to a SignalToAddress/WaitForAddress SVC - Suspended, ///< Thread is waiting due to process suspension -}; - -class Thread final : public KSynchronizationObject, public boost::intrusive::list_base_hook<> { - friend class KScheduler; - friend class Process; - -public: - explicit Thread(KernelCore& kernel); - ~Thread() override; - - using MutexWaitingThreads = std::vector<std::shared_ptr<Thread>>; - - using ThreadContext32 = Core::ARM_Interface::ThreadContext32; - using ThreadContext64 = Core::ARM_Interface::ThreadContext64; - - /** - * Creates and returns a new thread. The new thread is immediately scheduled - * @param system The instance of the whole system - * @param name The friendly name desired for the thread - * @param entry_point The address at which the thread should start execution - * @param priority The thread's priority - * @param arg User data to pass to the thread - * @param processor_id The ID(s) of the processors on which the thread is desired to be run - * @param stack_top The address of the thread's stack top - * @param owner_process The parent process for the thread, if null, it's a kernel thread - * @return A shared pointer to the newly created thread - */ - static ResultVal<std::shared_ptr<Thread>> Create(Core::System& system, ThreadType type_flags, - std::string name, VAddr entry_point, - u32 priority, u64 arg, s32 processor_id, - VAddr stack_top, Process* owner_process); - - /** - * Creates and returns a new thread. The new thread is immediately scheduled - * @param system The instance of the whole system - * @param name The friendly name desired for the thread - * @param entry_point The address at which the thread should start execution - * @param priority The thread's priority - * @param arg User data to pass to the thread - * @param processor_id The ID(s) of the processors on which the thread is desired to be run - * @param stack_top The address of the thread's stack top - * @param owner_process The parent process for the thread, if null, it's a kernel thread - * @param thread_start_func The function where the host context will start. - * @param thread_start_parameter The parameter which will passed to host context on init - * @return A shared pointer to the newly created thread - */ - static ResultVal<std::shared_ptr<Thread>> Create(Core::System& system, ThreadType type_flags, - std::string name, VAddr entry_point, - u32 priority, u64 arg, s32 processor_id, - VAddr stack_top, Process* owner_process, - std::function<void(void*)>&& thread_start_func, - void* thread_start_parameter); - - std::string GetName() const override { - return name; - } - - void SetName(std::string new_name) { - name = std::move(new_name); - } - - std::string GetTypeName() const override { - return "Thread"; - } - - static constexpr HandleType HANDLE_TYPE = HandleType::Thread; - HandleType GetHandleType() const override { - return HANDLE_TYPE; - } - - /** - * Gets the thread's current priority - * @return The current thread's priority - */ - [[nodiscard]] s32 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; - } - - /** - * Sets the thread's nominal priority. - * @param priority The new priority. - */ - void SetBasePriority(u32 priority); - - /// Changes the core that the thread is running or scheduled to run on. - [[nodiscard]] ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask); - - /** - * Gets the thread's thread ID - * @return The thread's ID - */ - [[nodiscard]] u64 GetThreadID() const { - return thread_id; - } - - /// Resumes a thread from waiting - void Wakeup(); - - 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 - /// waiting result to signal a canceled wait. The function will then resume - /// this thread. - /// - void CancelWait(); - - void SetSynchronizationResults(KSynchronizationObject* object, ResultCode result); - - void SetSyncedObject(KSynchronizationObject* object, ResultCode result) { - SetSynchronizationResults(object, result); - } - - ResultCode GetWaitResult(KSynchronizationObject** out) const { - *out = signaling_object; - return signaling_result; - } - - ResultCode GetSignalingResult() const { - return signaling_result; - } - - /** - * Stops a thread, invalidating it from further use - */ - void Stop(); - - /* - * Returns the Thread Local Storage address of the current thread - * @returns VAddr of the thread's TLS - */ - VAddr GetTLSAddress() const { - return tls_address; - } - - /* - * Returns the value of the TPIDR_EL0 Read/Write system register for this thread. - * @returns The value of the TPIDR_EL0 register. - */ - u64 GetTPIDR_EL0() const { - return tpidr_el0; - } - - /// Sets the value of the TPIDR_EL0 Read/Write system register for this thread. - void SetTPIDR_EL0(u64 value) { - tpidr_el0 = value; - } - - /* - * Returns the address of the current thread's command buffer, located in the TLS. - * @returns VAddr of the thread's command buffer. - */ - VAddr GetCommandBufferAddress() const; - - ThreadContext32& GetContext32() { - return context_32; - } - - const ThreadContext32& GetContext32() const { - return context_32; - } - - ThreadContext64& GetContext64() { - return context_64; - } - - const ThreadContext64& GetContext64() const { - return context_64; - } - - bool IsHLEThread() const { - return (type & THREADTYPE_HLE) != 0; - } - - bool IsSuspendThread() const { - return (type & THREADTYPE_SUSPEND) != 0; - } - - bool IsIdleThread() const { - return (type & THREADTYPE_IDLE) != 0; - } - - bool WasRunning() const { - return was_running; - } - - void SetWasRunning(bool value) { - was_running = value; - } - - std::shared_ptr<Common::Fiber>& GetHostContext(); - - ThreadState GetState() const { - return thread_state & ThreadState::Mask; - } - - ThreadState GetRawState() const { - return thread_state; - } - - void SetState(ThreadState state); - - s64 GetLastScheduledTick() const { - return last_scheduled_tick; - } - - void SetLastScheduledTick(s64 tick) { - last_scheduled_tick = tick; - } - - u64 GetTotalCPUTimeTicks() const { - return total_cpu_time_ticks; - } - - void UpdateCPUTimeTicks(u64 ticks) { - total_cpu_time_ticks += ticks; - } - - s32 GetProcessorID() const { - return processor_id; - } - - s32 GetActiveCore() const { - return GetProcessorID(); - } - - void SetProcessorID(s32 new_core) { - processor_id = new_core; - } - - void SetActiveCore(s32 new_core) { - processor_id = new_core; - } - - Process* GetOwnerProcess() { - return owner_process; - } - - const Process* GetOwnerProcess() const { - return owner_process; - } - - const MutexWaitingThreads& GetMutexWaitingThreads() const { - return wait_mutex_threads; - } - - Thread* GetLockOwner() const { - return lock_owner; - } - - void SetLockOwner(Thread* owner) { - lock_owner = owner; - } - - u32 GetIdealCore() const { - return ideal_core; - } - - const KAffinityMask& GetAffinityMask() const { - return affinity_mask; - } - - ResultCode SetActivity(ThreadActivity value); - - /// Sleeps this thread for the given amount of nanoseconds. - ResultCode Sleep(s64 nanoseconds); - - s64 GetYieldScheduleCount() const { - return schedule_count; - } - - void SetYieldScheduleCount(s64 count) { - schedule_count = count; - } - - bool IsRunning() const { - return is_running; - } - - void SetIsRunning(bool value) { - is_running = value; - } - - bool IsWaitCancelled() const { - return is_sync_cancelled; - } - - void ClearWaitCancelled() { - is_sync_cancelled = false; - } - - Handle GetGlobalHandle() const { - return global_handle; - } - - bool IsCancellable() const { - return is_cancellable; - } - - void SetCancellable() { - is_cancellable = true; - } - - void ClearCancellable() { - is_cancellable = false; - } - - bool IsTerminationRequested() const { - return will_be_terminated || GetRawState() == ThreadState::Terminated; - } - - bool IsPaused() const { - return pausing_state != 0; - } - - bool IsContinuousOnSVC() const { - return is_continuous_on_svc; - } - - void SetContinuousOnSVC(bool is_continuous) { - is_continuous_on_svc = is_continuous; - } - - bool IsPhantomMode() const { - return is_phantom_mode; - } - - void SetPhantomMode(bool phantom) { - is_phantom_mode = phantom; - } - - bool HasExited() const { - return has_exited; - } - - class QueueEntry { - public: - constexpr QueueEntry() = default; - - constexpr void Initialize() { - prev = nullptr; - next = nullptr; - } - - constexpr Thread* GetPrev() const { - return prev; - } - constexpr Thread* GetNext() const { - return next; - } - constexpr void SetPrev(Thread* thread) { - prev = thread; - } - constexpr void SetNext(Thread* thread) { - next = thread; - } - - private: - Thread* prev{}; - Thread* next{}; - }; - - QueueEntry& GetPriorityQueueEntry(s32 core) { - return per_core_priority_queue_entry[core]; - } - - const QueueEntry& GetPriorityQueueEntry(s32 core) const { - return per_core_priority_queue_entry[core]; - } - - s32 GetDisableDispatchCount() const { - return disable_count; - } - - void DisableDispatch() { - ASSERT(GetDisableDispatchCount() >= 0); - disable_count++; - } - - void EnableDispatch() { - ASSERT(GetDisableDispatchCount() > 0); - disable_count--; - } - - void SetWaitReasonForDebugging(ThreadWaitReasonForDebugging reason) { - wait_reason_for_debugging = reason; - } - - [[nodiscard]] ThreadWaitReasonForDebugging GetWaitReasonForDebugging() const { - return wait_reason_for_debugging; - } - - void SetWaitObjectsForDebugging(const std::span<KSynchronizationObject*>& objects) { - wait_objects_for_debugging.clear(); - wait_objects_for_debugging.reserve(objects.size()); - for (const auto& object : objects) { - wait_objects_for_debugging.emplace_back(object); - } - } - - [[nodiscard]] const std::vector<KSynchronizationObject*>& 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; - } - - void SetAddressKey(VAddr key) { - address_key = key; - } - - void SetAddressKey(VAddr key, u32 val) { - address_key = key; - address_key_value = val; - } - -private: - static constexpr size_t PriorityInheritanceCountMax = 10; - union SyncObjectBuffer { - std::array<KSynchronizationObject*, Svc::ArgumentHandleCountMax> sync_objects{}; - std::array<Handle, - Svc::ArgumentHandleCountMax*(sizeof(KSynchronizationObject*) / sizeof(Handle))> - handles; - constexpr SyncObjectBuffer() {} - }; - static_assert(sizeof(SyncObjectBuffer::sync_objects) == sizeof(SyncObjectBuffer::handles)); - - 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 <typename T> - requires( - std::same_as<T, Thread> || - std::same_as<T, LightCompareType>) 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<ConditionVariableComparator>; - -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 AddSchedulingFlag(ThreadSchedFlags flag); - void RemoveSchedulingFlag(ThreadSchedFlags flag); - void AddWaiterImpl(Thread* thread); - void RemoveWaiterImpl(Thread* thread); - static void RestorePriority(KernelCore& kernel, Thread* thread); - - Common::SpinLock context_guard{}; - ThreadContext32 context_32{}; - ThreadContext64 context_64{}; - std::shared_ptr<Common::Fiber> host_context{}; - - ThreadState thread_state = ThreadState::Initialized; - - u64 thread_id = 0; - - VAddr entry_point = 0; - VAddr stack_top = 0; - std::atomic_int disable_count = 0; - - ThreadType type; - - /// 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{}; - - /// Current thread priority. This may change over the course of the - /// thread's lifetime in order to facilitate priority inheritance. - s32 current_priority{}; - - u64 total_cpu_time_ticks = 0; ///< Total CPU running ticks. - s64 schedule_count{}; - s64 last_scheduled_tick{}; - - s32 processor_id = 0; - - VAddr tls_address = 0; ///< Virtual address of the Thread Local Storage of the thread - u64 tpidr_el0 = 0; ///< TPIDR_EL0 read/write system register. - - /// Process that owns this thread - 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<KSynchronizationObject*> wait_objects_for_debugging; - - /// The current mutex wait address. This is used for debugging only. - VAddr mutex_wait_address_for_debugging{}; - - /// The reason the thread is waiting. This is used for debugging only. - ThreadWaitReasonForDebugging wait_reason_for_debugging{}; - - KSynchronizationObject* 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{}; - - /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. - Handle global_handle = 0; - - KScheduler* scheduler = nullptr; - - std::array<QueueEntry, Core::Hardware::NUM_CPU_CORES> per_core_priority_queue_entry{}; - - u32 ideal_core{0xFFFFFFFF}; - KAffinityMask affinity_mask{}; - - s32 ideal_core_override = -1; - u32 affinity_override_count = 0; - - u32 pausing_state = 0; - bool is_running = false; - bool is_cancellable = false; - bool is_sync_cancelled = false; - - bool is_continuous_on_svc = false; - - bool will_be_terminated = false; - bool is_phantom_mode = false; - bool has_exited = false; - - 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<Thread>; - WaiterList waiter_list{}; - WaiterList pinned_waiter_list{}; - - std::string name; -}; - -} // namespace Kernel diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp index 832edd629..fd0630019 100644 --- a/src/core/hle/kernel/time_manager.cpp +++ b/src/core/hle/kernel/time_manager.cpp @@ -8,8 +8,8 @@ #include "core/core_timing_util.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/k_scheduler.h" +#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/thread.h" #include "core/hle/kernel/time_manager.h" namespace Kernel { @@ -18,50 +18,30 @@ 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) { - std::shared_ptr<Thread> thread; + std::shared_ptr<KThread> thread; { std::lock_guard lock{mutex}; - const auto proper_handle = static_cast<Handle>(thread_handle); - if (cancelled_events[proper_handle]) { - return; - } - thread = system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle); - } - - if (thread) { - // Thread can be null if process has exited - thread->Wakeup(); + thread = SharedFrom<KThread>(reinterpret_cast<KThread*>(thread_handle)); } + thread->Wakeup(); }); } -void TimeManager::ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 nanoseconds) { +void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) { std::lock_guard lock{mutex}; - event_handle = timetask->GetGlobalHandle(); if (nanoseconds > 0) { - ASSERT(timetask); - ASSERT(timetask->GetState() != ThreadState::Runnable); + ASSERT(thread); + ASSERT(thread->GetState() != ThreadState::Runnable); system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{nanoseconds}, - time_manager_event_type, event_handle); - } else { - event_handle = InvalidHandle; - } - cancelled_events[event_handle] = false; -} - -void TimeManager::UnscheduleTimeEvent(Handle event_handle) { - std::lock_guard lock{mutex}; - if (event_handle == InvalidHandle) { - return; + time_manager_event_type, + reinterpret_cast<uintptr_t>(thread)); } - system.CoreTiming().UnscheduleEvent(time_manager_event_type, event_handle); - cancelled_events[event_handle] = true; } -void TimeManager::CancelTimeEvent(Thread* time_task) { +void TimeManager::UnscheduleTimeEvent(KThread* thread) { std::lock_guard lock{mutex}; - const Handle event_handle = time_task->GetGlobalHandle(); - UnscheduleTimeEvent(event_handle); + system.CoreTiming().UnscheduleEvent(time_manager_event_type, + reinterpret_cast<uintptr_t>(thread)); } } // namespace Kernel diff --git a/src/core/hle/kernel/time_manager.h b/src/core/hle/kernel/time_manager.h index f39df39a0..0d7f05f30 100644 --- a/src/core/hle/kernel/time_manager.h +++ b/src/core/hle/kernel/time_manager.h @@ -20,7 +20,7 @@ struct EventType; namespace Kernel { -class Thread; +class KThread; /** * The `TimeManager` takes care of scheduling time events on threads and executes their TimeUp @@ -31,18 +31,14 @@ public: explicit TimeManager(Core::System& system); /// Schedule a time event on `timetask` thread that will expire in 'nanoseconds' - /// returns a non-invalid handle in `event_handle` if correctly scheduled - void ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 nanoseconds); + void ScheduleTimeEvent(KThread* time_task, s64 nanoseconds); /// Unschedule an existing time event - void UnscheduleTimeEvent(Handle event_handle); - - void CancelTimeEvent(Thread* time_task); + void UnscheduleTimeEvent(KThread* thread); private: Core::System& system; std::shared_ptr<Core::Timing::EventType> time_manager_event_type; - std::unordered_map<Handle, bool> cancelled_events; std::mutex mutex; }; diff --git a/src/core/hle/kernel/transfer_memory.h b/src/core/hle/kernel/transfer_memory.h index 05e9f7464..777799d12 100644 --- a/src/core/hle/kernel/transfer_memory.h +++ b/src/core/hle/kernel/transfer_memory.h @@ -72,6 +72,8 @@ public: /// is closed. ResultCode Reset(); + void Finalize() override {} + private: /// The base address for the memory managed by this instance. VAddr base_address{}; diff --git a/src/core/hle/kernel/writable_event.cpp b/src/core/hle/kernel/writable_event.cpp index fc2f7c424..142212ee4 100644 --- a/src/core/hle/kernel/writable_event.cpp +++ b/src/core/hle/kernel/writable_event.cpp @@ -4,10 +4,10 @@ #include <algorithm> #include "common/assert.h" +#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/object.h" #include "core/hle/kernel/readable_event.h" -#include "core/hle/kernel/thread.h" #include "core/hle/kernel/writable_event.h" namespace Kernel { @@ -38,8 +38,4 @@ void WritableEvent::Clear() { readable->Clear(); } -bool WritableEvent::IsSignaled() const { - return readable->IsSignaled(); -} - } // namespace Kernel diff --git a/src/core/hle/kernel/writable_event.h b/src/core/hle/kernel/writable_event.h index 6189cf65c..467eb2c21 100644 --- a/src/core/hle/kernel/writable_event.h +++ b/src/core/hle/kernel/writable_event.h @@ -46,7 +46,8 @@ public: void Signal(); void Clear(); - bool IsSignaled() const; + + void Finalize() override {} private: explicit WritableEvent(KernelCore& kernel); diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 41bd1497c..d42236a3a 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -1192,7 +1192,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_) {40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"}, {50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"}, {60, nullptr, "SetMediaPlaybackStateForApplication"}, - {65, nullptr, "IsGamePlayRecordingSupported"}, + {65, &IApplicationFunctions::IsGamePlayRecordingSupported, "IsGamePlayRecordingSupported"}, {66, &IApplicationFunctions::InitializeGamePlayRecording, "InitializeGamePlayRecording"}, {67, &IApplicationFunctions::SetGamePlayRecordingState, "SetGamePlayRecordingState"}, {68, nullptr, "RequestFlushGamePlayingMovieForDebug"}, @@ -1213,7 +1213,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_) {124, nullptr, "EnableApplicationAllThreadDumpOnCrash"}, {130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"}, {140, &IApplicationFunctions::GetFriendInvitationStorageChannelEvent, "GetFriendInvitationStorageChannelEvent"}, - {141, nullptr, "TryPopFromFriendInvitationStorageChannel"}, + {141, &IApplicationFunctions::TryPopFromFriendInvitationStorageChannel, "TryPopFromFriendInvitationStorageChannel"}, {150, nullptr, "GetNotificationStorageChannelEvent"}, {151, nullptr, "TryPopFromNotificationStorageChannel"}, {160, nullptr, "GetHealthWarningDisappearedSystemEvent"}, @@ -1480,6 +1480,16 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) { rb.Push(*res_code); } +void IApplicationFunctions::IsGamePlayRecordingSupported(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + constexpr bool gameplay_recording_supported = false; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(gameplay_recording_supported); +} + void IApplicationFunctions::InitializeGamePlayRecording(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_AM, "(STUBBED) called"); @@ -1631,6 +1641,14 @@ void IApplicationFunctions::GetFriendInvitationStorageChannelEvent(Kernel::HLERe rb.PushCopyObjects(friend_invitation_storage_channel_event.readable); } +void IApplicationFunctions::TryPopFromFriendInvitationStorageChannel( + Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERR_NO_DATA_IN_CHANNEL); +} + void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger, Core::System& system) { auto message_queue = std::make_shared<AppletMessageQueue>(system.Kernel()); diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 50737432c..f5db41ac8 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -266,6 +266,7 @@ private: void SetTerminateResult(Kernel::HLERequestContext& ctx); void GetDisplayVersion(Kernel::HLERequestContext& ctx); void GetDesiredLanguage(Kernel::HLERequestContext& ctx); + void IsGamePlayRecordingSupported(Kernel::HLERequestContext& ctx); void InitializeGamePlayRecording(Kernel::HLERequestContext& ctx); void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx); void NotifyRunning(Kernel::HLERequestContext& ctx); @@ -288,6 +289,7 @@ private: void GetPreviousProgramIndex(Kernel::HLERequestContext& ctx); void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx); void GetFriendInvitationStorageChannelEvent(Kernel::HLERequestContext& ctx); + void TryPopFromFriendInvitationStorageChannel(Kernel::HLERequestContext& ctx); bool launch_popped_application_specific = false; bool launch_popped_account_preselect = false; diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 02ca711fb..273a46265 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp @@ -58,7 +58,7 @@ public: {8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"}, {9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"}, {10, nullptr, "GetAudioOutPlayedSampleCount"}, - {11, nullptr, "FlushAudioOutBuffers"}, + {11, &IAudioOut::FlushAudioOutBuffers, "FlushAudioOutBuffers"}, {12, &IAudioOut::SetAudioOutVolume, "SetAudioOutVolume"}, {13, &IAudioOut::GetAudioOutVolume, "GetAudioOutVolume"}, }; @@ -185,6 +185,14 @@ private: rb.Push(static_cast<u32>(stream->GetQueueSize())); } + void FlushAudioOutBuffers(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Audio, "called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(stream->Flush()); + } + void SetAudioOutVolume(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const float volume = rp.Pop<float>(); diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index 265c986e2..0c227b135 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp @@ -946,20 +946,19 @@ void Controller_NPad::SetSixAxisEnabled(bool six_axis_status) { sixaxis_sensors_enabled = six_axis_status; } -void Controller_NPad::SetSixAxisFusionParameters(const DeviceHandle& handle, f32 parameter1, - f32 parameter2) { +void Controller_NPad::SetSixAxisFusionParameters(f32 parameter1, f32 parameter2) { sixaxis_fusion_parameter1 = parameter1; sixaxis_fusion_parameter2 = parameter2; } -std::pair<f32, f32> Controller_NPad::GetSixAxisFusionParameters(const DeviceHandle& handle) { +std::pair<f32, f32> Controller_NPad::GetSixAxisFusionParameters() { return { sixaxis_fusion_parameter1, sixaxis_fusion_parameter2, }; } -void Controller_NPad::ResetSixAxisFusionParameters(const DeviceHandle& handle) { +void Controller_NPad::ResetSixAxisFusionParameters() { sixaxis_fusion_parameter1 = 0.0f; sixaxis_fusion_parameter2 = 0.0f; } diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h index bfd06372a..2e13922b9 100644 --- a/src/core/hle/service/hid/controllers/npad.h +++ b/src/core/hle/service/hid/controllers/npad.h @@ -202,9 +202,9 @@ public: GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const; bool IsSixAxisSensorAtRest() const; void SetSixAxisEnabled(bool six_axis_status); - void SetSixAxisFusionParameters(const DeviceHandle& handle, f32 parameter1, f32 parameter2); - std::pair<f32, f32> GetSixAxisFusionParameters(const DeviceHandle& handle); - void ResetSixAxisFusionParameters(const DeviceHandle& handle); + void SetSixAxisFusionParameters(f32 parameter1, f32 parameter2); + std::pair<f32, f32> GetSixAxisFusionParameters(); + void ResetSixAxisFusionParameters(); LedPattern GetLedPattern(u32 npad_id); bool IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const; void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, u32 npad_id); diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 7fd3161e8..4cee4838c 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -126,14 +126,23 @@ void IAppletResource::UpdateControllers(std::uintptr_t user_data, controller->OnUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE); } + // If ns_late is higher than the update rate ignore the delay + if (ns_late > motion_update_ns) { + ns_late = {}; + } + core_timing.ScheduleEvent(pad_update_ns - ns_late, pad_update_event); } void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) { auto& core_timing = system.CoreTiming(); - for (const auto& controller : controllers) { - controller->OnMotionUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE); + controllers[static_cast<size_t>(HidController::NPad)]->OnMotionUpdate( + core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE); + + // If ns_late is higher than the update rate ignore the delay + if (ns_late > motion_update_ns) { + ns_late = {}; } core_timing.ScheduleEvent(motion_update_ns - ns_late, motion_update_event); @@ -520,6 +529,7 @@ void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) { Controller_NPad::DeviceHandle sixaxis_handle; u64 applet_resource_user_id; }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); const auto parameters{rp.PopRaw<Parameters>()}; @@ -542,19 +552,19 @@ void Hid::SetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { f32 parameter2; u64 applet_resource_user_id; }; + static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); const auto parameters{rp.PopRaw<Parameters>()}; applet_resource->GetController<Controller_NPad>(HidController::NPad) - .SetSixAxisFusionParameters(parameters.sixaxis_handle, parameters.parameter1, - parameters.parameter2); + .SetSixAxisFusionParameters(parameters.parameter1, parameters.parameter2); LOG_WARNING(Service_HID, - "(STUBBED) called, float1={}, float2={}, npad_type={}, npad_id={}, " - "device_index={}, applet_resource_user_id={}", - parameters.parameter1, parameters.parameter2, parameters.sixaxis_handle.npad_type, - parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index, - parameters.applet_resource_user_id); + "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, parameter1={}, " + "parameter2={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.parameter1, + parameters.parameter2, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -566,6 +576,7 @@ void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { Controller_NPad::DeviceHandle sixaxis_handle; u64 applet_resource_user_id; }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); f32 parameter1 = 0; f32 parameter2 = 0; @@ -573,13 +584,13 @@ void Hid::GetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { std::tie(parameter1, parameter2) = applet_resource->GetController<Controller_NPad>(HidController::NPad) - .GetSixAxisFusionParameters(parameters.sixaxis_handle); + .GetSixAxisFusionParameters(); - LOG_WARNING(Service_HID, - "(STUBBED) called, npad_type={}, npad_id={}, " - "device_index={}, applet_resource_user_id={}", - parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, - parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + LOG_WARNING( + Service_HID, + "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 4}; rb.Push(RESULT_SUCCESS); @@ -593,17 +604,18 @@ void Hid::ResetSixAxisSensorFusionParameters(Kernel::HLERequestContext& ctx) { Controller_NPad::DeviceHandle sixaxis_handle; u64 applet_resource_user_id; }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); const auto parameters{rp.PopRaw<Parameters>()}; applet_resource->GetController<Controller_NPad>(HidController::NPad) - .ResetSixAxisFusionParameters(parameters.sixaxis_handle); + .ResetSixAxisFusionParameters(); - LOG_WARNING(Service_HID, - "(STUBBED) called, npad_type={}, npad_id={}, " - "device_index={}, applet_resource_user_id={}", - parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, - parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); + LOG_WARNING( + Service_HID, + "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}", + parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id, + parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index 641bcadea..a515fdc60 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp @@ -8,9 +8,9 @@ #include "common/logging/log.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/readable_event.h" -#include "core/hle/kernel/thread.h" #include "core/hle/kernel/writable_event.h" #include "core/hle/lock.h" #include "core/hle/service/nfp/nfp.h" diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index ef5176bea..8372e170c 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -21,6 +21,93 @@ enum class RequestState : u32 { Connected = 3, }; +struct IpAddressSetting { + bool is_automatic{}; + Network::IPv4Address current_address{}; + Network::IPv4Address subnet_mask{}; + Network::IPv4Address gateway{}; +}; +static_assert(sizeof(IpAddressSetting) == 0xD, "IpAddressSetting has incorrect size."); + +struct DnsSetting { + bool is_automatic{}; + Network::IPv4Address primary_dns{}; + Network::IPv4Address secondary_dns{}; +}; +static_assert(sizeof(DnsSetting) == 0x9, "DnsSetting has incorrect size."); + +struct ProxySetting { + bool enabled{}; + INSERT_PADDING_BYTES(1); + u16 port{}; + std::array<char, 0x64> proxy_server{}; + bool automatic_auth_enabled{}; + std::array<char, 0x20> user{}; + std::array<char, 0x20> password{}; + INSERT_PADDING_BYTES(1); +}; +static_assert(sizeof(ProxySetting) == 0xAA, "ProxySetting has incorrect size."); + +struct IpSettingData { + IpAddressSetting ip_address_setting{}; + DnsSetting dns_setting{}; + ProxySetting proxy_setting{}; + u16 mtu{}; +}; +static_assert(sizeof(IpSettingData) == 0xC2, "IpSettingData has incorrect size."); + +struct SfWirelessSettingData { + u8 ssid_length{}; + std::array<char, 0x20> ssid{}; + u8 unknown_1{}; + u8 unknown_2{}; + u8 unknown_3{}; + std::array<char, 0x41> passphrase{}; +}; +static_assert(sizeof(SfWirelessSettingData) == 0x65, "SfWirelessSettingData has incorrect size."); + +struct NifmWirelessSettingData { + u8 ssid_length{}; + std::array<char, 0x21> ssid{}; + u8 unknown_1{}; + INSERT_PADDING_BYTES(1); + u32 unknown_2{}; + u32 unknown_3{}; + std::array<char, 0x41> passphrase{}; + INSERT_PADDING_BYTES(3); +}; +static_assert(sizeof(NifmWirelessSettingData) == 0x70, + "NifmWirelessSettingData has incorrect size."); + +#pragma pack(push, 1) +struct SfNetworkProfileData { + IpSettingData ip_setting_data{}; + u128 uuid{}; + std::array<char, 0x40> network_name{}; + u8 unknown_1{}; + u8 unknown_2{}; + u8 unknown_3{}; + u8 unknown_4{}; + SfWirelessSettingData wireless_setting_data{}; + INSERT_PADDING_BYTES(1); +}; +static_assert(sizeof(SfNetworkProfileData) == 0x17C, "SfNetworkProfileData has incorrect size."); + +struct NifmNetworkProfileData { + u128 uuid{}; + std::array<char, 0x40> network_name{}; + u32 unknown_1{}; + u32 unknown_2{}; + u8 unknown_3{}; + u8 unknown_4{}; + INSERT_PADDING_BYTES(2); + NifmWirelessSettingData wireless_setting_data{}; + IpSettingData ip_setting_data{}; +}; +static_assert(sizeof(NifmNetworkProfileData) == 0x18E, + "NifmNetworkProfileData has incorrect size."); +#pragma pack(pop) + class IScanRequest final : public ServiceFramework<IScanRequest> { public: explicit IScanRequest(Core::System& system_) : ServiceFramework{system_, "IScanRequest"} { @@ -128,7 +215,11 @@ private: void GetAppletInfo(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NIFM, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 8}; + std::vector<u8> out_buffer(ctx.GetWriteBufferSize()); + + ctx.WriteBuffer(out_buffer); + + IPC::ResponseBuilder rb{ctx, 5}; rb.Push(RESULT_SUCCESS); rb.Push<u32>(0); rb.Push<u32>(0); @@ -179,6 +270,46 @@ private: rb.Push(RESULT_SUCCESS); rb.PushIpcInterface<IRequest>(system); } + void GetCurrentNetworkProfile(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + + const SfNetworkProfileData network_profile_data{ + .ip_setting_data{ + .ip_address_setting{ + .is_automatic{true}, + .current_address{192, 168, 1, 100}, + .subnet_mask{255, 255, 255, 0}, + .gateway{192, 168, 1, 1}, + }, + .dns_setting{ + .is_automatic{true}, + .primary_dns{1, 1, 1, 1}, + .secondary_dns{1, 0, 0, 1}, + }, + .proxy_setting{ + .enabled{false}, + .port{}, + .proxy_server{}, + .automatic_auth_enabled{}, + .user{}, + .password{}, + }, + .mtu{1500}, + }, + .uuid{0xdeadbeef, 0xdeadbeef}, + .network_name{"yuzu Network"}, + .wireless_setting_data{ + .ssid_length{12}, + .ssid{"yuzu Network"}, + .passphrase{"yuzupassword"}, + }, + }; + + ctx.WriteBuffer(network_profile_data); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NIFM, "(STUBBED) called"); @@ -210,6 +341,34 @@ private: rb.PushIpcInterface<INetworkProfile>(system); rb.PushRaw<u128>(uuid); } + void GetCurrentIpConfigInfo(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_NIFM, "(STUBBED) called"); + + struct IpConfigInfo { + IpAddressSetting ip_address_setting; + DnsSetting dns_setting; + }; + static_assert(sizeof(IpConfigInfo) == sizeof(IpAddressSetting) + sizeof(DnsSetting), + "IpConfigInfo has incorrect size."); + + const IpConfigInfo ip_config_info{ + .ip_address_setting{ + .is_automatic{true}, + .current_address{192, 168, 1, 100}, + .subnet_mask{255, 255, 255, 0}, + .gateway{192, 168, 1, 1}, + }, + .dns_setting{ + .is_automatic{true}, + .primary_dns{1, 1, 1, 1}, + .secondary_dns{1, 0, 0, 1}, + }, + }; + + IPC::ResponseBuilder rb{ctx, 2 + sizeof(IpConfigInfo) / sizeof(u32)}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw<IpConfigInfo>(ip_config_info); + } void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service_NIFM, "(STUBBED) called"); @@ -248,7 +407,7 @@ IGeneralService::IGeneralService(Core::System& system_) {1, &IGeneralService::GetClientId, "GetClientId"}, {2, &IGeneralService::CreateScanRequest, "CreateScanRequest"}, {4, &IGeneralService::CreateRequest, "CreateRequest"}, - {5, nullptr, "GetCurrentNetworkProfile"}, + {5, &IGeneralService::GetCurrentNetworkProfile, "GetCurrentNetworkProfile"}, {6, nullptr, "EnumerateNetworkInterfaces"}, {7, nullptr, "EnumerateNetworkProfiles"}, {8, nullptr, "GetNetworkProfile"}, @@ -258,7 +417,7 @@ IGeneralService::IGeneralService(Core::System& system_) {12, &IGeneralService::GetCurrentIpAddress, "GetCurrentIpAddress"}, {13, nullptr, "GetCurrentAccessPointOld"}, {14, &IGeneralService::CreateTemporaryNetworkProfile, "CreateTemporaryNetworkProfile"}, - {15, nullptr, "GetCurrentIpConfigInfo"}, + {15, &IGeneralService::GetCurrentIpConfigInfo, "GetCurrentIpConfigInfo"}, {16, nullptr, "SetWirelessCommunicationEnabled"}, {17, &IGeneralService::IsWirelessCommunicationEnabled, "IsWirelessCommunicationEnabled"}, {18, nullptr, "GetInternetConnectionStatus"}, diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp index cc23b001c..1328b64d0 100644 --- a/src/core/hle/service/nvdrv/interface.cpp +++ b/src/core/hle/service/nvdrv/interface.cpp @@ -6,9 +6,9 @@ #include "common/logging/log.h" #include "core/core.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/readable_event.h" -#include "core/hle/kernel/thread.h" #include "core/hle/kernel/writable_event.h" #include "core/hle/service/nvdrv/interface.h" #include "core/hle/service/nvdrv/nvdata.h" diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp index b417624c9..d5b3b17a5 100644 --- a/src/core/hle/service/prepo/prepo.cpp +++ b/src/core/hle/service/prepo/prepo.cpp @@ -23,11 +23,11 @@ public: {10101, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::Old>, "SaveReportWithUserOld"}, {10102, &PlayReport::SaveReport<Core::Reporter::PlayReportType::Old2>, "SaveReportOld2"}, {10103, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::Old2>, "SaveReportWithUserOld2"}, - {10104, nullptr, "SaveReport"}, - {10105, nullptr, "SaveReportWithUser"}, - {10200, nullptr, "RequestImmediateTransmission"}, - {10300, nullptr, "GetTransmissionStatus"}, - {10400, nullptr, "GetSystemSessionId"}, + {10104, &PlayReport::SaveReport<Core::Reporter::PlayReportType::New>, "SaveReport"}, + {10105, &PlayReport::SaveReportWithUser<Core::Reporter::PlayReportType::New>, "SaveReportWithUser"}, + {10200, &PlayReport::RequestImmediateTransmission, "RequestImmediateTransmission"}, + {10300, &PlayReport::GetTransmissionStatus, "GetTransmissionStatus"}, + {10400, &PlayReport::GetSystemSessionId, "GetSystemSessionId"}, {20100, &PlayReport::SaveSystemReport, "SaveSystemReport"}, {20101, &PlayReport::SaveSystemReportWithUser, "SaveSystemReportWithUser"}, {20200, nullptr, "SetOperationMode"}, @@ -59,16 +59,22 @@ private: IPC::RequestParser rp{ctx}; const auto process_id = rp.PopRaw<u64>(); - std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)}; - if constexpr (Type == Core::Reporter::PlayReportType::Old2) { - data.emplace_back(ctx.ReadBuffer(1)); - } + const auto data1 = ctx.ReadBuffer(0); + const auto data2 = [ctx] { + if (ctx.CanReadBuffer(1)) { + return ctx.ReadBuffer(1); + } - LOG_DEBUG(Service_PREPO, "called, type={:02X}, process_id={:016X}, data1_size={:016X}", - Type, process_id, data[0].size()); + return std::vector<u8>{}; + }(); + + LOG_DEBUG(Service_PREPO, + "called, type={:02X}, process_id={:016X}, data1_size={:016X}, data2_size={:016X}", + Type, process_id, data1.size(), data2.size()); const auto& reporter{system.GetReporter()}; - reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), data, process_id); + reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), {data1, data2}, + process_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); @@ -79,35 +85,67 @@ private: IPC::RequestParser rp{ctx}; const auto user_id = rp.PopRaw<u128>(); const auto process_id = rp.PopRaw<u64>(); - std::vector<std::vector<u8>> data{ctx.ReadBuffer(0)}; - if constexpr (Type == Core::Reporter::PlayReportType::Old2) { - const auto read_buffer_count = - ctx.BufferDescriptorX().size() + ctx.BufferDescriptorA().size(); - if (read_buffer_count > 1) { - data.emplace_back(ctx.ReadBuffer(1)); + const auto data1 = ctx.ReadBuffer(0); + const auto data2 = [ctx] { + if (ctx.CanReadBuffer(1)) { + return ctx.ReadBuffer(1); } - } - LOG_DEBUG( - Service_PREPO, - "called, type={:02X}, user_id={:016X}{:016X}, process_id={:016X}, data1_size={:016X}", - Type, user_id[1], user_id[0], process_id, data[0].size()); + return std::vector<u8>{}; + }(); + + LOG_DEBUG(Service_PREPO, + "called, type={:02X}, user_id={:016X}{:016X}, process_id={:016X}, " + "data1_size={:016X}, data2_size={:016X}", + Type, user_id[1], user_id[0], process_id, data1.size(), data2.size()); const auto& reporter{system.GetReporter()}; - reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), data, process_id, - user_id); + reporter.SavePlayReport(Type, system.CurrentProcess()->GetTitleID(), {data1, data2}, + process_id, user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(RESULT_SUCCESS); } + void RequestImmediateTransmission(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_PREPO, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } + + void GetTransmissionStatus(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_PREPO, "(STUBBED) called"); + + constexpr s32 status = 0; + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(status); + } + + void GetSystemSessionId(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_PREPO, "(STUBBED) called"); + + constexpr u64 system_session_id = 0; + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push(system_session_id); + } + void SaveSystemReport(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto title_id = rp.PopRaw<u64>(); const auto data1 = ctx.ReadBuffer(0); - const auto data2 = ctx.ReadBuffer(1); + const auto data2 = [ctx] { + if (ctx.CanReadBuffer(1)) { + return ctx.ReadBuffer(1); + } + + return std::vector<u8>{}; + }(); LOG_DEBUG(Service_PREPO, "called, title_id={:016X}, data1_size={:016X}, data2_size={:016X}", title_id, data1.size(), data2.size()); @@ -125,7 +163,13 @@ private: const auto title_id = rp.PopRaw<u64>(); const auto data1 = ctx.ReadBuffer(0); - const auto data2 = ctx.ReadBuffer(1); + const auto data2 = [ctx] { + if (ctx.CanReadBuffer(1)) { + return ctx.ReadBuffer(1); + } + + return std::vector<u8>{}; + }(); LOG_DEBUG(Service_PREPO, "called, user_id={:016X}{:016X}, title_id={:016X}, data1_size={:016X}, " diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index ff2a5b1db..1da56bc27 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -11,10 +11,10 @@ #include "core/hle/ipc.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/client_port.h" +#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/server_port.h" -#include "core/hle/kernel/thread.h" #include "core/hle/service/acc/acc.h" #include "core/hle/service/am/am.h" #include "core/hle/service/aoc/aoc_u.h" diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index 2b824059d..0b306b87a 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp @@ -13,7 +13,7 @@ #include "common/microprofile.h" #include "common/thread.h" #include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/thread.h" +#include "core/hle/kernel/k_thread.h" #include "core/hle/service/sockets/bsd.h" #include "core/hle/service/sockets/sockets_translate.h" #include "core/network/network.h" @@ -255,6 +255,25 @@ void BSD::GetSockName(Kernel::HLERequestContext& ctx) { rb.Push<u32>(static_cast<u32>(write_buffer.size())); } +void BSD::GetSockOpt(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const s32 fd = rp.Pop<s32>(); + const u32 level = rp.Pop<u32>(); + const auto optname = static_cast<OptName>(rp.Pop<u32>()); + + LOG_WARNING(Service, "(STUBBED) called. fd={} level={} optname=0x{:x}", fd, level, optname); + + std::vector<u8> optval(ctx.GetWriteBufferSize()); + + ctx.WriteBuffer(optval); + + IPC::ResponseBuilder rb{ctx, 5}; + rb.Push(RESULT_SUCCESS); + rb.Push<s32>(-1); + rb.PushEnum(Errno::NOTCONN); + rb.Push<u32>(static_cast<u32>(optval.size())); +} + void BSD::Listen(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const s32 fd = rp.Pop<s32>(); @@ -401,6 +420,16 @@ void BSD::Close(Kernel::HLERequestContext& ctx) { BuildErrnoResponse(ctx, CloseImpl(fd)); } +void BSD::EventFd(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u64 initval = rp.Pop<u64>(); + const u32 flags = rp.Pop<u32>(); + + LOG_WARNING(Service, "(STUBBED) called. initval={}, flags={}", initval, flags); + + BuildErrnoResponse(ctx, Errno::SUCCESS); +} + template <typename Work> void BSD::ExecuteWork(Kernel::HLERequestContext& ctx, Work work) { work.Execute(this); @@ -812,7 +841,7 @@ BSD::BSD(Core::System& system_, const char* name) : ServiceFramework{system_, na {14, &BSD::Connect, "Connect"}, {15, &BSD::GetPeerName, "GetPeerName"}, {16, &BSD::GetSockName, "GetSockName"}, - {17, nullptr, "GetSockOpt"}, + {17, &BSD::GetSockOpt, "GetSockOpt"}, {18, &BSD::Listen, "Listen"}, {19, nullptr, "Ioctl"}, {20, &BSD::Fcntl, "Fcntl"}, @@ -826,7 +855,7 @@ BSD::BSD(Core::System& system_, const char* name) : ServiceFramework{system_, na {28, nullptr, "GetResourceStatistics"}, {29, nullptr, "RecvMMsg"}, {30, nullptr, "SendMMsg"}, - {31, nullptr, "EventFd"}, + {31, &BSD::EventFd, "EventFd"}, {32, nullptr, "RegisterResourceStatisticsName"}, {33, nullptr, "Initialize2"}, }; diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h index 6da0bfeb2..1d2df9c61 100644 --- a/src/core/hle/service/sockets/bsd.h +++ b/src/core/hle/service/sockets/bsd.h @@ -125,6 +125,7 @@ private: void Connect(Kernel::HLERequestContext& ctx); void GetPeerName(Kernel::HLERequestContext& ctx); void GetSockName(Kernel::HLERequestContext& ctx); + void GetSockOpt(Kernel::HLERequestContext& ctx); void Listen(Kernel::HLERequestContext& ctx); void Fcntl(Kernel::HLERequestContext& ctx); void SetSockOpt(Kernel::HLERequestContext& ctx); @@ -135,6 +136,7 @@ private: void SendTo(Kernel::HLERequestContext& ctx); void Write(Kernel::HLERequestContext& ctx); void Close(Kernel::HLERequestContext& ctx); + void EventFd(Kernel::HLERequestContext& ctx); template <typename Work> void ExecuteWork(Kernel::HLERequestContext& ctx, Work work); diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index abc753d5d..18629dd7e 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp @@ -121,7 +121,7 @@ private: }; ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal( - Kernel::Thread* thread, Clock::SystemClockContext user_context, + Kernel::KThread* thread, Clock::SystemClockContext user_context, Clock::SystemClockContext network_context, u8 type, Clock::ClockSnapshot& clock_snapshot) { auto& time_manager{system.GetTimeManager()}; diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h index 975a8ae5b..4154c7ee9 100644 --- a/src/core/hle/service/time/time.h +++ b/src/core/hle/service/time/time.h @@ -39,7 +39,7 @@ public: private: ResultCode GetClockSnapshotFromSystemClockContextInternal( - Kernel::Thread* thread, Clock::SystemClockContext user_context, + Kernel::KThread* thread, Clock::SystemClockContext user_context, Clock::SystemClockContext network_context, u8 type, Clock::ClockSnapshot& cloc_snapshot); diff --git a/src/core/hle/service/time/time_sharedmemory.h b/src/core/hle/service/time/time_sharedmemory.h index 5976b2046..e0c3e63da 100644 --- a/src/core/hle/service/time/time_sharedmemory.h +++ b/src/core/hle/service/time/time_sharedmemory.h @@ -6,8 +6,8 @@ #include "common/common_types.h" #include "common/uuid.h" +#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/shared_memory.h" -#include "core/hle/kernel/thread.h" #include "core/hle/service/time/clock_types.h" namespace Service::Time { diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 968cd16b6..f3de2c428 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -18,8 +18,8 @@ #include "common/swap.h" #include "core/core_timing.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/readable_event.h" -#include "core/hle/kernel/thread.h" #include "core/hle/kernel/writable_event.h" #include "core/hle/service/nvdrv/nvdata.h" #include "core/hle/service/nvdrv/nvdrv.h" diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index ccf8cc153..f976d0a9c 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -15,9 +15,9 @@ #include "core/file_sys/romfs_factory.h" #include "core/file_sys/vfs_offset.h" #include "core/hle/kernel/code_set.h" +#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/memory/page_table.h" #include "core/hle/kernel/process.h" -#include "core/hle/kernel/thread.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/loader/nro.h" #include "core/loader/nso.h" @@ -219,8 +219,8 @@ AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process, Core::Sy } is_loaded = true; - return {ResultStatus::Success, - LoadParameters{Kernel::THREADPRIO_DEFAULT, Core::Memory::DEFAULT_STACK_SIZE}}; + return {ResultStatus::Success, LoadParameters{Kernel::KThread::DefaultThreadPriority, + Core::Memory::DEFAULT_STACK_SIZE}}; } ResultStatus AppLoader_NRO::ReadIcon(std::vector<u8>& buffer) { diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 95b6f339a..ea347ea83 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -15,9 +15,9 @@ #include "core/core.h" #include "core/file_sys/patch_manager.h" #include "core/hle/kernel/code_set.h" +#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/memory/page_table.h" #include "core/hle/kernel/process.h" -#include "core/hle/kernel/thread.h" #include "core/loader/nso.h" #include "core/memory.h" #include "core/settings.h" @@ -179,8 +179,8 @@ AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process, Core::Sy LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address); is_loaded = true; - return {ResultStatus::Success, - LoadParameters{Kernel::THREADPRIO_DEFAULT, Core::Memory::DEFAULT_STACK_SIZE}}; + return {ResultStatus::Success, LoadParameters{Kernel::KThread::DefaultThreadPriority, + Core::Memory::DEFAULT_STACK_SIZE}}; } ResultStatus AppLoader_NSO::ReadNSOModules(Modules& modules) { diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 39306509a..2ae5196e0 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -70,6 +70,9 @@ void LogSettings() { log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue()); log_setting("Audio_OutputDevice", values.audio_device_id); log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd); + log_setting("DataStorage_CacheDir", Common::FS::GetUserPath(Common::FS::UserPath::CacheDir)); + log_setting("DataStorage_ConfigDir", Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir)); + log_setting("DataStorage_LoadDir", Common::FS::GetUserPath(Common::FS::UserPath::LoadDir)); log_setting("DataStorage_NandDir", Common::FS::GetUserPath(Common::FS::UserPath::NANDDir)); log_setting("DataStorage_SdmcDir", Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir)); log_setting("Debugging_ProgramArgs", values.program_args); diff --git a/src/input_common/analog_from_button.cpp b/src/input_common/analog_from_button.cpp index 40b516f85..07a0fa4a1 100755 --- a/src/input_common/analog_from_button.cpp +++ b/src/input_common/analog_from_button.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <atomic> #include <chrono> #include <cmath> #include <thread> @@ -20,13 +21,16 @@ public: : up(std::move(up_)), down(std::move(down_)), left(std::move(left_)), right(std::move(right_)), modifier(std::move(modifier_)), modifier_scale(modifier_scale_), modifier_angle(modifier_angle_) { + update_thread_running.store(true); update_thread = std::thread(&Analog::UpdateStatus, this); } ~Analog() override { - update_thread_running = false; - if (update_thread.joinable()) { - update_thread.join(); + if (update_thread_running.load()) { + update_thread_running.store(false); + if (update_thread.joinable()) { + update_thread.join(); + } } } @@ -58,7 +62,7 @@ public: } void UpdateStatus() { - while (update_thread_running) { + while (update_thread_running.load()) { const float coef = modifier->GetStatus() ? modifier_scale : 1.0f; bool r = right->GetStatus(); @@ -160,7 +164,7 @@ private: float angle{}; float amplitude{}; std::thread update_thread; - bool update_thread_running{true}; + std::atomic<bool> update_thread_running{}; }; std::unique_ptr<Input::AnalogDevice> AnalogFromButton::Create(const Common::ParamPackage& params) { diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index f5cdf548e..c841f3cd7 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -4,6 +4,7 @@ #include "common/alignment.h" #include "common/assert.h" +#include "common/logging/log.h" #include "core/core.h" #include "core/hle/kernel/memory/page_table.h" #include "core/hle/kernel/process.h" @@ -38,6 +39,12 @@ GPUVAddr MemoryManager::UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std } GPUVAddr MemoryManager::Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size) { + const auto it = std::ranges::lower_bound(map_ranges, gpu_addr, {}, &MapRange::first); + if (it != map_ranges.end() && it->first == gpu_addr) { + it->second = size; + } else { + map_ranges.insert(it, MapRange{gpu_addr, size}); + } return UpdateRange(gpu_addr, cpu_addr, size); } @@ -52,10 +59,16 @@ GPUVAddr MemoryManager::MapAllocate32(VAddr cpu_addr, std::size_t size) { } void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) { - if (!size) { + if (size == 0) { return; } - + const auto it = std::ranges::lower_bound(map_ranges, gpu_addr, {}, &MapRange::first); + if (it != map_ranges.end()) { + ASSERT(it->first == gpu_addr); + map_ranges.erase(it); + } else { + UNREACHABLE_MSG("Unmapping non-existent GPU address=0x{:x}", gpu_addr); + } // Flush and invalidate through the GPU interface, to be asynchronous if possible. const std::optional<VAddr> cpu_addr = GpuToCpuAddress(gpu_addr); ASSERT(cpu_addr); @@ -218,6 +231,12 @@ const u8* MemoryManager::GetPointer(GPUVAddr gpu_addr) const { return system.Memory().GetPointer(*address); } +size_t MemoryManager::BytesToMapEnd(GPUVAddr gpu_addr) const noexcept { + auto it = std::ranges::upper_bound(map_ranges, gpu_addr, {}, &MapRange::first); + --it; + return it->second - (gpu_addr - it->first); +} + void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size) const { std::size_t remaining_size{size}; std::size_t page_index{gpu_src_addr >> page_bits}; diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index a52fbbd8c..b468a67de 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -85,6 +85,9 @@ public: [[nodiscard]] u8* GetPointer(GPUVAddr addr); [[nodiscard]] const u8* GetPointer(GPUVAddr addr) const; + /// Returns the number of bytes until the end of the memory map containing the given GPU address + [[nodiscard]] size_t BytesToMapEnd(GPUVAddr gpu_addr) const noexcept; + /** * ReadBlock and WriteBlock are full read and write operations over virtual * GPU Memory. It's important to use these when GPU memory may not be continuous @@ -151,6 +154,9 @@ private: VideoCore::RasterizerInterface* rasterizer = nullptr; std::vector<PageEntry> page_table; + + using MapRange = std::pair<GPUVAddr, size_t>; + std::vector<MapRange> map_ranges; }; } // namespace Tegra diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index 4cd43e425..15f2987eb 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -6,10 +6,12 @@ #include <atomic> #include <condition_variable> +#include <cstddef> #include <memory> #include <stack> #include <thread> #include <utility> +#include "common/alignment.h" #include "common/common_types.h" #include "common/threadsafe_queue.h" #include "video_core/vulkan_common/vulkan_wrapper.h" @@ -130,12 +132,11 @@ private: using FuncType = TypedCommand<T>; static_assert(sizeof(FuncType) < sizeof(data), "Lambda is too large"); + command_offset = Common::AlignUp(command_offset, alignof(FuncType)); if (command_offset > sizeof(data) - sizeof(FuncType)) { return false; } - - Command* current_last = last; - + Command* const current_last = last; last = new (data.data() + command_offset) FuncType(std::move(command)); if (current_last) { @@ -143,7 +144,6 @@ private: } else { first = last; } - command_offset += sizeof(FuncType); return true; } @@ -156,8 +156,8 @@ private: Command* first = nullptr; Command* last = nullptr; - std::size_t command_offset = 0; - std::array<u8, 0x8000> data{}; + size_t command_offset = 0; + alignas(std::max_align_t) std::array<u8, 0x8000> data{}; }; struct State { diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index 567a36d9b..422022d02 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp @@ -190,12 +190,16 @@ void ConfigureInput::ApplyConfiguration() { // This emulates a delay between disconnecting and reconnecting controllers as some games // do not respond to a change in controller type if it was instantaneous. using namespace std::chrono_literals; - std::this_thread::sleep_for(60ms); + std::this_thread::sleep_for(150ms); for (auto* controller : player_controllers) { controller->TryConnectSelectedController(); } + // This emulates a delay between disconnecting and reconnecting controllers as some games + // do not respond to a change in controller type if it was instantaneous. + std::this_thread::sleep_for(150ms); + advanced->ApplyConfiguration(); const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue(); diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp index 13f0351d4..fbe36046b 100644 --- a/src/yuzu/configuration/configure_input_player.cpp +++ b/src/yuzu/configuration/configure_input_player.cpp @@ -579,11 +579,11 @@ void ConfigureInputPlayer::ApplyConfiguration() { // Apply configuration for handheld if (player_index == 0) { auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; + const auto handheld_connected = handheld.connected; if (player.controller_type == Settings::ControllerType::Handheld) { handheld = player; } - handheld.connected = ui->groupConnectedController->isChecked() && - player.controller_type == Settings::ControllerType::Handheld; + handheld.connected = handheld_connected; } } @@ -595,6 +595,18 @@ void ConfigureInputPlayer::TryConnectSelectedController() { const auto player_connected = ui->groupConnectedController->isChecked() && controller_type != Settings::ControllerType::Handheld; + // Connect Handheld depending on Player 1's controller configuration. + if (player_index == 0 && controller_type == Settings::ControllerType::Handheld) { + auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; + const auto handheld_connected = ui->groupConnectedController->isChecked() && + controller_type == Settings::ControllerType::Handheld; + // Connect only if handheld is going from disconnected to connected + if (!handheld.connected && handheld_connected) { + UpdateController(controller_type, HANDHELD_INDEX, true); + } + handheld.connected = handheld_connected; + } + if (player.controller_type == controller_type && player.connected == player_connected) { // Set vibration devices in the event that the input device has changed. ConfigureVibration::SetVibrationDevices(player_index); @@ -606,22 +618,11 @@ void ConfigureInputPlayer::TryConnectSelectedController() { ConfigureVibration::SetVibrationDevices(player_index); - // Connect/Disconnect Handheld depending on Player 1's controller configuration. - if (player_index == 0) { - auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; - if (controller_type == Settings::ControllerType::Handheld) { - handheld = player; - } - handheld.connected = ui->groupConnectedController->isChecked() && - controller_type == Settings::ControllerType::Handheld; - UpdateController(Settings::ControllerType::Handheld, HANDHELD_INDEX, handheld.connected); - } - if (!player.connected) { return; } - UpdateController(controller_type, player_index, player_connected); + UpdateController(controller_type, player_index, true); } void ConfigureInputPlayer::TryDisconnectSelectedController() { @@ -632,11 +633,28 @@ void ConfigureInputPlayer::TryDisconnectSelectedController() { const auto player_connected = ui->groupConnectedController->isChecked() && controller_type != Settings::ControllerType::Handheld; + // Disconnect Handheld depending on Player 1's controller configuration. + if (player_index == 0 && player.controller_type == Settings::ControllerType::Handheld) { + const auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX]; + const auto handheld_connected = ui->groupConnectedController->isChecked() && + controller_type == Settings::ControllerType::Handheld; + // Disconnect only if handheld is going from connected to disconnected + if (handheld.connected && !handheld_connected) { + UpdateController(controller_type, HANDHELD_INDEX, false); + } + return; + } + // Do not do anything if the controller configuration has not changed. if (player.controller_type == controller_type && player.connected == player_connected) { return; } + // Do not disconnect if the controller is already disconnected + if (!player.connected) { + return; + } + // Disconnect the controller first. UpdateController(controller_type, player_index, false); } diff --git a/src/yuzu/configuration/configure_profile_manager.cpp b/src/yuzu/configuration/configure_profile_manager.cpp index d102a43af..51647a028 100644 --- a/src/yuzu/configuration/configure_profile_manager.cpp +++ b/src/yuzu/configuration/configure_profile_manager.cpp @@ -116,8 +116,8 @@ ConfigureProfileManager ::ConfigureProfileManager(QWidget* parent) scene = new QGraphicsScene; ui->current_user_icon->setScene(scene); - SetConfiguration(); RetranslateUI(); + SetConfiguration(); } ConfigureProfileManager::~ConfigureProfileManager() = default; diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index a93b5d3c2..0e5156dcc 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp @@ -15,10 +15,11 @@ #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/k_thread.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/thread.h" +#include "core/hle/kernel/svc_types.h" #include "core/memory.h" namespace { @@ -90,9 +91,9 @@ std::size_t WaitTreeItem::Row() const { std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList() { std::vector<std::unique_ptr<WaitTreeThread>> item_list; std::size_t row = 0; - auto add_threads = [&](const std::vector<std::shared_ptr<Kernel::Thread>>& threads) { + auto add_threads = [&](const std::vector<std::shared_ptr<Kernel::KThread>>& threads) { for (std::size_t i = 0; i < threads.size(); ++i) { - if (!threads[i]->IsHLEThread()) { + if (threads[i]->GetThreadTypeForDebugging() == Kernel::ThreadType::User) { item_list.push_back(std::make_unique<WaitTreeThread>(*threads[i])); item_list.back()->row = row; } @@ -117,7 +118,7 @@ WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address, const Kernel::HandleTa : mutex_address(mutex_address) { mutex_value = Core::System::GetInstance().Memory().Read32(mutex_address); owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Svc::HandleWaitMask); - owner = handle_table.Get<Kernel::Thread>(owner_handle); + owner = handle_table.Get<Kernel::KThread>(owner_handle); } WaitTreeMutexInfo::~WaitTreeMutexInfo() = default; @@ -139,7 +140,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeMutexInfo::GetChildren() cons return list; } -WaitTreeCallstack::WaitTreeCallstack(const Kernel::Thread& thread) : thread(thread) {} +WaitTreeCallstack::WaitTreeCallstack(const Kernel::KThread& thread) : thread(thread) {} WaitTreeCallstack::~WaitTreeCallstack() = default; QString WaitTreeCallstack::GetText() const { @@ -149,7 +150,7 @@ QString WaitTreeCallstack::GetText() const { std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() const { std::vector<std::unique_ptr<WaitTreeItem>> list; - if (thread.IsHLEThread()) { + if (thread.GetThreadTypeForDebugging() != Kernel::ThreadType::User) { return list; } @@ -194,7 +195,7 @@ std::unique_ptr<WaitTreeSynchronizationObject> WaitTreeSynchronizationObject::ma case Kernel::HandleType::ReadableEvent: return std::make_unique<WaitTreeEvent>(static_cast<const Kernel::ReadableEvent&>(object)); case Kernel::HandleType::Thread: - return std::make_unique<WaitTreeThread>(static_cast<const Kernel::Thread&>(object)); + return std::make_unique<WaitTreeThread>(static_cast<const Kernel::KThread&>(object)); default: return std::make_unique<WaitTreeSynchronizationObject>(object); } @@ -231,21 +232,17 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeObjectList::GetChildren() con return list; } -WaitTreeThread::WaitTreeThread(const Kernel::Thread& thread) +WaitTreeThread::WaitTreeThread(const Kernel::KThread& thread) : WaitTreeSynchronizationObject(thread) {} WaitTreeThread::~WaitTreeThread() = default; QString WaitTreeThread::GetText() const { - const auto& thread = static_cast<const Kernel::Thread&>(object); + const auto& thread = static_cast<const Kernel::KThread&>(object); QString status; switch (thread.GetState()) { case Kernel::ThreadState::Runnable: - if (!thread.IsPaused()) { - if (thread.WasRunning()) { - status = tr("running"); - } else { - status = tr("ready"); - } + if (!thread.IsSuspended()) { + status = tr("runnable"); } else { status = tr("paused"); } @@ -297,15 +294,11 @@ QString WaitTreeThread::GetText() const { QColor WaitTreeThread::GetColor() const { const std::size_t color_index = IsDarkTheme() ? 1 : 0; - const auto& thread = static_cast<const Kernel::Thread&>(object); + const auto& thread = static_cast<const Kernel::KThread&>(object); switch (thread.GetState()) { case Kernel::ThreadState::Runnable: - if (!thread.IsPaused()) { - if (thread.WasRunning()) { - return QColor(WaitTreeColors[0][color_index]); - } else { - return QColor(WaitTreeColors[1][color_index]); - } + if (!thread.IsSuspended()) { + return QColor(WaitTreeColors[0][color_index]); } else { return QColor(WaitTreeColors[2][color_index]); } @@ -336,27 +329,21 @@ QColor WaitTreeThread::GetColor() const { std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const { std::vector<std::unique_ptr<WaitTreeItem>> list(WaitTreeSynchronizationObject::GetChildren()); - const auto& thread = static_cast<const Kernel::Thread&>(object); + const auto& thread = static_cast<const Kernel::KThread&>(object); QString processor; - switch (thread.GetProcessorID()) { - case Kernel::ThreadProcessorId::THREADPROCESSORID_IDEAL: + switch (thread.GetActiveCore()) { + case Kernel::Svc::IdealCoreUseProcessValue: processor = tr("ideal"); break; - case Kernel::ThreadProcessorId::THREADPROCESSORID_0: - case Kernel::ThreadProcessorId::THREADPROCESSORID_1: - case Kernel::ThreadProcessorId::THREADPROCESSORID_2: - case Kernel::ThreadProcessorId::THREADPROCESSORID_3: - processor = tr("core %1").arg(thread.GetProcessorID()); - break; default: - processor = tr("Unknown processor %1").arg(thread.GetProcessorID()); + processor = tr("core %1").arg(thread.GetActiveCore()); break; } list.push_back(std::make_unique<WaitTreeText>(tr("processor = %1").arg(processor))); - list.push_back( - std::make_unique<WaitTreeText>(tr("ideal core = %1").arg(thread.GetIdealCore()))); + list.push_back(std::make_unique<WaitTreeText>( + tr("ideal core = %1").arg(thread.GetIdealCoreForDebugging()))); list.push_back(std::make_unique<WaitTreeText>( tr("affinity mask = %1").arg(thread.GetAffinityMask().GetAffinityMask()))); list.push_back(std::make_unique<WaitTreeText>(tr("thread id = %1").arg(thread.GetThreadID()))); @@ -390,7 +377,7 @@ WaitTreeEvent::WaitTreeEvent(const Kernel::ReadableEvent& object) : WaitTreeSynchronizationObject(object) {} WaitTreeEvent::~WaitTreeEvent() = default; -WaitTreeThreadList::WaitTreeThreadList(const std::vector<Kernel::Thread*>& list) +WaitTreeThreadList::WaitTreeThreadList(const std::vector<Kernel::KThread*>& 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..b202c5567 100644 --- a/src/yuzu/debugger/wait_tree.h +++ b/src/yuzu/debugger/wait_tree.h @@ -19,8 +19,8 @@ class EmuThread; namespace Kernel { class HandleTable; class KSynchronizationObject; +class KThread; class ReadableEvent; -class Thread; } // namespace Kernel class WaitTreeThread; @@ -83,20 +83,20 @@ private: VAddr mutex_address; u32 mutex_value; Kernel::Handle owner_handle; - std::shared_ptr<Kernel::Thread> owner; + std::shared_ptr<Kernel::KThread> owner; }; class WaitTreeCallstack : public WaitTreeExpandableItem { Q_OBJECT public: - explicit WaitTreeCallstack(const Kernel::Thread& thread); + explicit WaitTreeCallstack(const Kernel::KThread& thread); ~WaitTreeCallstack() override; QString GetText() const override; std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; private: - const Kernel::Thread& thread; + const Kernel::KThread& thread; }; class WaitTreeSynchronizationObject : public WaitTreeExpandableItem { @@ -131,7 +131,7 @@ private: class WaitTreeThread : public WaitTreeSynchronizationObject { Q_OBJECT public: - explicit WaitTreeThread(const Kernel::Thread& thread); + explicit WaitTreeThread(const Kernel::KThread& thread); ~WaitTreeThread() override; QString GetText() const override; @@ -149,14 +149,14 @@ public: class WaitTreeThreadList : public WaitTreeExpandableItem { Q_OBJECT public: - explicit WaitTreeThreadList(const std::vector<Kernel::Thread*>& list); + explicit WaitTreeThreadList(const std::vector<Kernel::KThread*>& list); ~WaitTreeThreadList() override; QString GetText() const override; std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; private: - const std::vector<Kernel::Thread*>& thread_list; + const std::vector<Kernel::KThread*>& thread_list; }; class WaitTreeModel : public QAbstractItemModel { diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index e76141125..886e6e9d2 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1039,8 +1039,6 @@ bool GMainWindow::LoadROM(const QString& filename, std::size_t program_index) { std::make_unique<QtWebBrowser>(*this), // Web Browser }); - system.RegisterHostThread(); - const Core::System::ResultStatus result{ system.Load(*render_window, filename.toStdString(), program_index)}; |