Merge pull request #5779 from bunnei/kthread-rewrite

Rewrite KThread to be more accurate
This commit is contained in:
bunnei 2021-01-29 23:06:40 -08:00 committed by GitHub
commit a4526c4e1a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
68 changed files with 2984 additions and 1958 deletions

View file

@ -97,10 +97,27 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
#define R_UNLESS(expr, res) \ #define R_UNLESS(expr, res) \
{ \ { \
if (!(expr)) { \ if (!(expr)) { \
if (res.IsError()) { \
LOG_ERROR(Kernel, "Failed with result: {}", res.raw); \
} \
return res; \ 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 { namespace Common {
[[nodiscard]] constexpr u32 MakeMagic(char a, char b, char c, char d) { [[nodiscard]] constexpr u32 MakeMagic(char a, char b, char c, char d) {

View file

@ -160,6 +160,8 @@ add_library(core STATIC
hle/kernel/k_affinity_mask.h hle/kernel/k_affinity_mask.h
hle/kernel/k_condition_variable.cpp hle/kernel/k_condition_variable.cpp
hle/kernel/k_condition_variable.h 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_priority_queue.h
hle/kernel/k_scheduler.cpp hle/kernel/k_scheduler.cpp
hle/kernel/k_scheduler.h 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_scoped_scheduler_lock_and_sleep.h
hle/kernel/k_synchronization_object.cpp hle/kernel/k_synchronization_object.cpp
hle/kernel/k_synchronization_object.h 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.cpp
hle/kernel/kernel.h hle/kernel/kernel.h
hle/kernel/memory/address_space_info.cpp hle/kernel/memory/address_space_info.cpp
@ -216,8 +221,6 @@ add_library(core STATIC
hle/kernel/svc_results.h hle/kernel/svc_results.h
hle/kernel/svc_types.h hle/kernel/svc_types.h
hle/kernel/svc_wrap.h hle/kernel/svc_wrap.h
hle/kernel/thread.cpp
hle/kernel/thread.h
hle/kernel/time_manager.cpp hle/kernel/time_manager.cpp
hle/kernel/time_manager.h hle/kernel/time_manager.h
hle/kernel/transfer_memory.cpp hle/kernel/transfer_memory.cpp

View file

@ -255,6 +255,9 @@ void ARM_Dynarmic_32::ChangeProcessorID(std::size_t new_core_id) {
} }
void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) { void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) {
if (!jit) {
return;
}
Dynarmic::A32::Context context; Dynarmic::A32::Context context;
jit->SaveContext(context); jit->SaveContext(context);
ctx.cpu_registers = context.Regs(); ctx.cpu_registers = context.Regs();
@ -264,6 +267,9 @@ void ARM_Dynarmic_32::SaveContext(ThreadContext32& ctx) {
} }
void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) { void ARM_Dynarmic_32::LoadContext(const ThreadContext32& ctx) {
if (!jit) {
return;
}
Dynarmic::A32::Context context; Dynarmic::A32::Context context;
context.Regs() = ctx.cpu_registers; context.Regs() = ctx.cpu_registers;
context.ExtRegs() = ctx.extension_registers; context.ExtRegs() = ctx.extension_registers;

View file

@ -294,6 +294,9 @@ void ARM_Dynarmic_64::ChangeProcessorID(std::size_t new_core_id) {
} }
void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) { void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) {
if (!jit) {
return;
}
ctx.cpu_registers = jit->GetRegisters(); ctx.cpu_registers = jit->GetRegisters();
ctx.sp = jit->GetSP(); ctx.sp = jit->GetSP();
ctx.pc = jit->GetPC(); ctx.pc = jit->GetPC();
@ -305,6 +308,9 @@ void ARM_Dynarmic_64::SaveContext(ThreadContext64& ctx) {
} }
void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) { void ARM_Dynarmic_64::LoadContext(const ThreadContext64& ctx) {
if (!jit) {
return;
}
jit->SetRegisters(ctx.cpu_registers); jit->SetRegisters(ctx.cpu_registers);
jit->SetSP(ctx.sp); jit->SetSP(ctx.sp);
jit->SetPC(ctx.pc); jit->SetPC(ctx.pc);

View file

@ -28,10 +28,10 @@
#include "core/hardware_interrupt_manager.h" #include "core/hardware_interrupt_manager.h"
#include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/k_scheduler.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/kernel.h"
#include "core/hle/kernel/physical_core.h" #include "core/hle/kernel/physical_core.h"
#include "core/hle/kernel/process.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/am/applets/applets.h"
#include "core/hle/service/apm/controller.h" #include "core/hle/service/apm/controller.h"
#include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/filesystem/filesystem.h"

View file

@ -11,9 +11,9 @@
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/cpu_manager.h" #include "core/cpu_manager.h"
#include "core/hle/kernel/k_scheduler.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/kernel.h"
#include "core/hle/kernel/physical_core.h" #include "core/hle/kernel/physical_core.h"
#include "core/hle/kernel/thread.h"
#include "video_core/gpu.h" #include "video_core/gpu.h"
namespace Core { namespace Core {
@ -147,7 +147,7 @@ void CpuManager::MultiCoreRunSuspendThread() {
while (true) { while (true) {
auto core = kernel.GetCurrentHostThreadID(); auto core = kernel.GetCurrentHostThreadID();
auto& scheduler = *kernel.CurrentScheduler(); 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); Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[core].host_context);
ASSERT(scheduler.ContextSwitchPending()); ASSERT(scheduler.ContextSwitchPending());
ASSERT(core == kernel.GetCurrentHostThreadID()); ASSERT(core == kernel.GetCurrentHostThreadID());
@ -208,7 +208,6 @@ void CpuManager::SingleCoreRunGuestThread() {
void CpuManager::SingleCoreRunGuestLoop() { void CpuManager::SingleCoreRunGuestLoop() {
auto& kernel = system.Kernel(); auto& kernel = system.Kernel();
auto* thread = kernel.CurrentScheduler()->GetCurrentThread();
while (true) { while (true) {
auto* physical_core = &kernel.CurrentPhysicalCore(); auto* physical_core = &kernel.CurrentPhysicalCore();
system.EnterDynarmicProfile(); system.EnterDynarmicProfile();
@ -217,9 +216,9 @@ void CpuManager::SingleCoreRunGuestLoop() {
physical_core = &kernel.CurrentPhysicalCore(); physical_core = &kernel.CurrentPhysicalCore();
} }
system.ExitDynarmicProfile(); system.ExitDynarmicProfile();
thread->SetPhantomMode(true); kernel.SetIsPhantomModeForSingleCore(true);
system.CoreTiming().Advance(); system.CoreTiming().Advance();
thread->SetPhantomMode(false); kernel.SetIsPhantomModeForSingleCore(false);
physical_core->ArmInterface().ClearExclusiveState(); physical_core->ArmInterface().ClearExclusiveState();
PreemptSingleCore(); PreemptSingleCore();
auto& scheduler = kernel.Scheduler(current_core); auto& scheduler = kernel.Scheduler(current_core);
@ -245,7 +244,7 @@ void CpuManager::SingleCoreRunSuspendThread() {
while (true) { while (true) {
auto core = kernel.GetCurrentHostThreadID(); auto core = kernel.GetCurrentHostThreadID();
auto& scheduler = *kernel.CurrentScheduler(); 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); Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[0].host_context);
ASSERT(scheduler.ContextSwitchPending()); ASSERT(scheduler.ContextSwitchPending());
ASSERT(core == kernel.GetCurrentHostThreadID()); ASSERT(core == kernel.GetCurrentHostThreadID());
@ -255,22 +254,23 @@ void CpuManager::SingleCoreRunSuspendThread() {
void CpuManager::PreemptSingleCore(bool from_running_enviroment) { void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
{ {
auto& scheduler = system.Kernel().Scheduler(current_core); auto& kernel = system.Kernel();
Kernel::Thread* current_thread = scheduler.GetCurrentThread(); auto& scheduler = kernel.Scheduler(current_core);
Kernel::KThread* current_thread = scheduler.GetCurrentThread();
if (idle_count >= 4 || from_running_enviroment) { if (idle_count >= 4 || from_running_enviroment) {
if (!from_running_enviroment) { if (!from_running_enviroment) {
system.CoreTiming().Idle(); system.CoreTiming().Idle();
idle_count = 0; idle_count = 0;
} }
current_thread->SetPhantomMode(true); kernel.SetIsPhantomModeForSingleCore(true);
system.CoreTiming().Advance(); system.CoreTiming().Advance();
current_thread->SetPhantomMode(false); kernel.SetIsPhantomModeForSingleCore(false);
} }
current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES); current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES);
system.CoreTiming().ResetTicks(); system.CoreTiming().ResetTicks();
scheduler.Unload(scheduler.GetCurrentThread()); 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()); 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); auto& scheduler = system.Kernel().Scheduler(current_core);
scheduler.Reload(scheduler.GetCurrentThread()); scheduler.Reload(scheduler.GetCurrentThread());
auto* currrent_thread2 = scheduler.GetCurrentThread(); if (!scheduler.IsIdle()) {
if (!currrent_thread2->IsIdleThread()) {
idle_count = 0; idle_count = 0;
} }
} }

View file

@ -4,8 +4,10 @@
#pragma once #pragma once
#include <array>
#include <tuple> #include <tuple>
#include "common/bit_util.h"
#include "common/common_types.h" #include "common/common_types.h"
namespace Core { 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 u64 CNTFREQ = 19200000; // Switch's hardware clock speed
constexpr u32 NUM_CPU_CORES = 4; // Number of CPU Cores constexpr u32 NUM_CPU_CORES = 4; // Number of CPU Cores
} // namespace Hardware // Virtual to Physical core map.
constexpr std::array<s32, Common::BitSize<u64>()> VirtualToPhysicalCoreMap{
constexpr u32 INVALID_HOST_THREAD_ID = 0xFFFFFFFF; 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,
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();
}
}; };
} // namespace Hardware
} // namespace Core } // namespace Core

View file

@ -51,6 +51,8 @@ public:
*/ */
void ConnectionClosed(); void ConnectionClosed();
void Finalize() override {}
private: private:
std::shared_ptr<ServerPort> server_port; ///< ServerPort associated with this client port. 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 u32 max_sessions = 0; ///< Maximum number of simultaneous sessions the port can have

View file

@ -5,9 +5,9 @@
#include "core/hle/kernel/client_session.h" #include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/errors.h" #include "core/hle/kernel/errors.h"
#include "core/hle/kernel/hle_ipc.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/server_session.h"
#include "core/hle/kernel/session.h" #include "core/hle/kernel/session.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/result.h" #include "core/hle/result.h"
namespace Kernel { namespace Kernel {
@ -38,7 +38,7 @@ ResultVal<std::shared_ptr<ClientSession>> ClientSession::Create(KernelCore& kern
return MakeResult(std::move(client_session)); 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::Memory::Memory& memory,
Core::Timing::CoreTiming& core_timing) { Core::Timing::CoreTiming& core_timing) {
// Keep ServerSession alive until we're done working with it. // Keep ServerSession alive until we're done working with it.

View file

@ -24,7 +24,7 @@ namespace Kernel {
class KernelCore; class KernelCore;
class Session; class Session;
class Thread; class KThread;
class ClientSession final : public KSynchronizationObject { class ClientSession final : public KSynchronizationObject {
public: public:
@ -46,11 +46,13 @@ public:
return HANDLE_TYPE; 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); Core::Timing::CoreTiming& core_timing);
bool IsSignaled() const override; bool IsSignaled() const override;
void Finalize() override {}
private: private:
static ResultVal<std::shared_ptr<ClientSession>> Create(KernelCore& kernel, static ResultVal<std::shared_ptr<ClientSession>> Create(KernelCore& kernel,
std::shared_ptr<Session> parent, std::shared_ptr<Session> parent,

View file

@ -17,12 +17,12 @@ GlobalSchedulerContext::GlobalSchedulerContext(KernelCore& kernel)
GlobalSchedulerContext::~GlobalSchedulerContext() = default; 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}; std::scoped_lock lock{global_list_guard};
thread_list.push_back(std::move(thread)); 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}; std::scoped_lock lock{global_list_guard};
thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread), thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
thread_list.end()); thread_list.end());

View file

@ -12,7 +12,8 @@
#include "core/hardware_properties.h" #include "core/hardware_properties.h"
#include "core/hle/kernel/k_priority_queue.h" #include "core/hle/kernel/k_priority_queue.h"
#include "core/hle/kernel/k_scheduler_lock.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 { namespace Kernel {
@ -20,8 +21,12 @@ class KernelCore;
class SchedulerLock; class SchedulerLock;
using KSchedulerPriorityQueue = using KSchedulerPriorityQueue =
KPriorityQueue<Thread, Core::Hardware::NUM_CPU_CORES, THREADPRIO_LOWEST, THREADPRIO_HIGHEST>; KPriorityQueue<KThread, Core::Hardware::NUM_CPU_CORES, Svc::LowestThreadPriority,
constexpr s32 HighestCoreMigrationAllowedPriority = 2; Svc::HighestThreadPriority>;
static constexpr s32 HighestCoreMigrationAllowedPriority = 2;
static_assert(Svc::LowestThreadPriority >= HighestCoreMigrationAllowedPriority);
static_assert(Svc::HighestThreadPriority <= HighestCoreMigrationAllowedPriority);
class GlobalSchedulerContext final { class GlobalSchedulerContext final {
friend class KScheduler; friend class KScheduler;
@ -33,13 +38,13 @@ public:
~GlobalSchedulerContext(); ~GlobalSchedulerContext();
/// Adds a new thread to the scheduler /// 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 /// 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 /// 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; return thread_list;
} }
@ -74,7 +79,7 @@ private:
LockType scheduler_lock; LockType scheduler_lock;
/// Lists all thread ids that aren't deleted/etc. /// 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{}; Common::SpinLock global_list_guard{};
}; };

View file

@ -9,9 +9,9 @@
#include "core/hle/kernel/errors.h" #include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/k_scheduler.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/kernel.h"
#include "core/hle/kernel/process.h" #include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h"
namespace Kernel { namespace Kernel {
namespace { namespace {
@ -89,6 +89,10 @@ ResultCode HandleTable::Close(Handle handle) {
const u16 slot = GetSlot(handle); const u16 slot = GetSlot(handle);
if (objects[slot].use_count() == 1) {
objects[slot]->Finalize();
}
objects[slot] = nullptr; objects[slot] = nullptr;
generations[slot] = next_free_slot; generations[slot] = next_free_slot;

View file

@ -19,12 +19,12 @@
#include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.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/kernel.h"
#include "core/hle/kernel/object.h" #include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h" #include "core/hle/kernel/process.h"
#include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/server_session.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/time_manager.h"
#include "core/hle/kernel/writable_event.h" #include "core/hle/kernel/writable_event.h"
#include "core/memory.h" #include "core/memory.h"
@ -48,7 +48,7 @@ void SessionRequestHandler::ClientDisconnected(
HLERequestContext::HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory, HLERequestContext::HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory,
std::shared_ptr<ServerSession> server_session, std::shared_ptr<ServerSession> server_session,
std::shared_ptr<Thread> thread) std::shared_ptr<KThread> thread)
: server_session(std::move(server_session)), : server_session(std::move(server_session)),
thread(std::move(thread)), kernel{kernel}, memory{memory} { thread(std::move(thread)), kernel{kernel}, memory{memory} {
cmd_buf[0] = 0; cmd_buf[0] = 0;
@ -182,7 +182,7 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const HandleTabl
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) { ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(KThread& thread) {
auto& owner_process = *thread.GetOwnerProcess(); auto& owner_process = *thread.GetOwnerProcess();
auto& handle_table = owner_process.GetHandleTable(); auto& handle_table = owner_process.GetHandleTable();

View file

@ -40,7 +40,7 @@ class HLERequestContext;
class KernelCore; class KernelCore;
class Process; class Process;
class ServerSession; class ServerSession;
class Thread; class KThread;
class ReadableEvent; class ReadableEvent;
class WritableEvent; class WritableEvent;
@ -110,7 +110,7 @@ class HLERequestContext {
public: public:
explicit HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory, explicit HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory,
std::shared_ptr<ServerSession> session, std::shared_ptr<ServerSession> session,
std::shared_ptr<Thread> thread); std::shared_ptr<KThread> thread);
~HLERequestContext(); ~HLERequestContext();
/// Returns a pointer to the IPC command buffer for this request. /// Returns a pointer to the IPC command buffer for this request.
@ -126,15 +126,12 @@ public:
return server_session; 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. /// Populates this context with data from the requesting process/thread.
ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table, ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table,
u32_le* src_cmdbuf); u32_le* src_cmdbuf);
/// Writes data from this context back to the requesting process/thread. /// Writes data from this context back to the requesting process/thread.
ResultCode WriteToOutgoingCommandBuffer(Thread& thread); ResultCode WriteToOutgoingCommandBuffer(KThread& thread);
u32_le GetCommand() const { u32_le GetCommand() const {
return command; return command;
@ -267,11 +264,11 @@ public:
std::string Description() const; std::string Description() const;
Thread& GetThread() { KThread& GetThread() {
return *thread; return *thread;
} }
const Thread& GetThread() const { const KThread& GetThread() const {
return *thread; return *thread;
} }
@ -286,7 +283,7 @@ private:
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf; std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
std::shared_ptr<Kernel::ServerSession> server_session; 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 // 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> move_objects;
boost::container::small_vector<std::shared_ptr<Object>, 8> copy_objects; boost::container::small_vector<std::shared_ptr<Object>, 8> copy_objects;

View file

@ -7,9 +7,9 @@
#include "core/hle/kernel/k_address_arbiter.h" #include "core/hle/kernel/k_address_arbiter.h"
#include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.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/kernel.h"
#include "core/hle/kernel/svc_results.h" #include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/time_manager.h" #include "core/hle/kernel/time_manager.h"
#include "core/memory.h" #include "core/memory.h"
@ -96,7 +96,7 @@ ResultCode KAddressArbiter::Signal(VAddr addr, s32 count) {
auto it = thread_tree.nfind_light({addr, -1}); auto it = thread_tree.nfind_light({addr, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) { (it->GetAddressArbiterKey() == addr)) {
Thread* target_thread = std::addressof(*it); KThread* target_thread = std::addressof(*it);
target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS); target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
ASSERT(target_thread->IsWaitingForAddressArbiter()); ASSERT(target_thread->IsWaitingForAddressArbiter());
@ -125,7 +125,7 @@ ResultCode KAddressArbiter::SignalAndIncrementIfEqual(VAddr addr, s32 value, s32
auto it = thread_tree.nfind_light({addr, -1}); auto it = thread_tree.nfind_light({addr, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) { (it->GetAddressArbiterKey() == addr)) {
Thread* target_thread = std::addressof(*it); KThread* target_thread = std::addressof(*it);
target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS); target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
ASSERT(target_thread->IsWaitingForAddressArbiter()); ASSERT(target_thread->IsWaitingForAddressArbiter());
@ -215,7 +215,7 @@ ResultCode KAddressArbiter::SignalAndModifyByWaitingCountIfEqual(VAddr addr, s32
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetAddressArbiterKey() == addr)) { (it->GetAddressArbiterKey() == addr)) {
Thread* target_thread = std::addressof(*it); KThread* target_thread = std::addressof(*it);
target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS); target_thread->SetSyncedObject(nullptr, RESULT_SUCCESS);
ASSERT(target_thread->IsWaitingForAddressArbiter()); 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) { ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement, s64 timeout) {
// Prepare to wait. // Prepare to wait.
Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
Handle timer = InvalidHandle;
{ {
KScopedSchedulerLockAndSleep slp(kernel, timer, cur_thread, timeout); KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
// Check that the thread isn't terminating. // Check that the thread isn't terminating.
if (cur_thread->IsTerminationRequested()) { if (cur_thread->IsTerminationRequested()) {
@ -280,10 +279,7 @@ ResultCode KAddressArbiter::WaitIfLessThan(VAddr addr, s32 value, bool decrement
} }
// Cancel the timer wait. // Cancel the timer wait.
if (timer != InvalidHandle) { kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
auto& time_manager = kernel.TimeManager();
time_manager.UnscheduleTimeEvent(timer);
}
// Remove from the address arbiter. // 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) { ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
// Prepare to wait. // Prepare to wait.
Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
Handle timer = InvalidHandle;
{ {
KScopedSchedulerLockAndSleep slp(kernel, timer, cur_thread, timeout); KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
// Check that the thread isn't terminating. // Check that the thread isn't terminating.
if (cur_thread->IsTerminationRequested()) { if (cur_thread->IsTerminationRequested()) {
@ -344,10 +339,7 @@ ResultCode KAddressArbiter::WaitIfEqual(VAddr addr, s32 value, s64 timeout) {
} }
// Cancel the timer wait. // Cancel the timer wait.
if (timer != InvalidHandle) { kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
auto& time_manager = kernel.TimeManager();
time_manager.UnscheduleTimeEvent(timer);
}
// Remove from the address arbiter. // Remove from the address arbiter.
{ {

View file

@ -10,11 +10,11 @@
#include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.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_synchronization_object.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h" #include "core/hle/kernel/process.h"
#include "core/hle/kernel/svc_common.h" #include "core/hle/kernel/svc_common.h"
#include "core/hle/kernel/svc_results.h" #include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/thread.h"
#include "core/memory.h" #include "core/memory.h"
namespace Kernel { namespace Kernel {
@ -66,7 +66,7 @@ KConditionVariable::KConditionVariable(Core::System& system_)
KConditionVariable::~KConditionVariable() = default; KConditionVariable::~KConditionVariable() = default;
ResultCode KConditionVariable::SignalToAddress(VAddr addr) { ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
Thread* owner_thread = kernel.CurrentScheduler()->GetCurrentThread(); KThread* owner_thread = kernel.CurrentScheduler()->GetCurrentThread();
// Signal the address. // Signal the address.
{ {
@ -74,7 +74,7 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
// Remove waiter thread. // Remove waiter thread.
s32 num_waiters{}; s32 num_waiters{};
Thread* next_owner_thread = KThread* next_owner_thread =
owner_thread->RemoveWaiterByKey(std::addressof(num_waiters), addr); owner_thread->RemoveWaiterByKey(std::addressof(num_waiters), addr);
// Determine the next tag. // Determine the next tag.
@ -103,11 +103,11 @@ ResultCode KConditionVariable::SignalToAddress(VAddr addr) {
} }
ResultCode KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) { 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. // Wait for the address.
{ {
std::shared_ptr<Thread> owner_thread; std::shared_ptr<KThread> owner_thread;
ASSERT(!owner_thread); ASSERT(!owner_thread);
{ {
KScopedSchedulerLock sl(kernel); 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); R_UNLESS(test_tag == (handle | Svc::HandleWaitMask), RESULT_SUCCESS);
// Get the lock owner thread. // 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); R_UNLESS(owner_thread, Svc::ResultInvalidHandle);
// Update the lock. // 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. // Remove the thread as a waiter from the lock owner.
{ {
KScopedSchedulerLock sl(kernel); KScopedSchedulerLock sl(kernel);
Thread* owner_thread = cur_thread->GetLockOwner(); KThread* owner_thread = cur_thread->GetLockOwner();
if (owner_thread != nullptr) { if (owner_thread != nullptr) {
owner_thread->RemoveWaiter(cur_thread); 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)); return cur_thread->GetWaitResult(std::addressof(dummy));
} }
Thread* KConditionVariable::SignalImpl(Thread* thread) { KThread* KConditionVariable::SignalImpl(KThread* thread) {
// Check pre-conditions. // Check pre-conditions.
ASSERT(kernel.GlobalSchedulerContext().IsLocked()); 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 (can_access) {
if (prev_tag == InvalidHandle) { if (prev_tag == InvalidHandle) {
// If nobody held the lock previously, we're all good. // If nobody held the lock previously, we're all good.
@ -182,7 +182,7 @@ Thread* KConditionVariable::SignalImpl(Thread* thread) {
thread->Wakeup(); thread->Wakeup();
} else { } else {
// Get the previous owner. // Get the previous owner.
auto owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<Thread>( auto owner_thread = kernel.CurrentProcess()->GetHandleTable().Get<KThread>(
prev_tag & ~Svc::HandleWaitMask); prev_tag & ~Svc::HandleWaitMask);
if (owner_thread) { 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 // TODO(bunnei): This should just be Thread once we implement KAutoObject instead of using
// std::shared_ptr. // std::shared_ptr.
std::vector<std::shared_ptr<Thread>> thread_list; std::vector<std::shared_ptr<KThread>> thread_list;
std::array<Thread*, MaxThreads> thread_array; std::array<KThread*, MaxThreads> thread_array;
s32 num_to_close{}; s32 num_to_close{};
// Perform signaling. // Perform signaling.
@ -222,9 +222,9 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
auto it = thread_tree.nfind_light({cv_key, -1}); auto it = thread_tree.nfind_light({cv_key, -1});
while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) && while ((it != thread_tree.end()) && (count <= 0 || num_waiters < count) &&
(it->GetConditionVariableKey() == cv_key)) { (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) { if (num_to_close < MaxThreads) {
thread_array[num_to_close++] = thread; thread_array[num_to_close++] = thread;
} else { } else {
@ -257,11 +257,10 @@ void KConditionVariable::Signal(u64 cv_key, s32 count) {
ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) { ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout) {
// Prepare to wait. // Prepare to wait.
Thread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread(); KThread* cur_thread = kernel.CurrentScheduler()->GetCurrentThread();
Handle timer = InvalidHandle;
{ {
KScopedSchedulerLockAndSleep slp(kernel, timer, cur_thread, timeout); KScopedSchedulerLockAndSleep slp{kernel, cur_thread, timeout};
// Set the synced object. // Set the synced object.
cur_thread->SetSyncedObject(nullptr, Svc::ResultTimedOut); cur_thread->SetSyncedObject(nullptr, Svc::ResultTimedOut);
@ -276,7 +275,7 @@ ResultCode KConditionVariable::Wait(VAddr addr, u64 key, u32 value, s64 timeout)
{ {
// Remove waiter thread. // Remove waiter thread.
s32 num_waiters{}; s32 num_waiters{};
Thread* next_owner_thread = KThread* next_owner_thread =
cur_thread->RemoveWaiterByKey(std::addressof(num_waiters), addr); cur_thread->RemoveWaiterByKey(std::addressof(num_waiters), addr);
// Update for the next owner thread. // 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. // Cancel the timer wait.
if (timer != InvalidHandle) { kernel.TimeManager().UnscheduleTimeEvent(cur_thread);
auto& time_manager = kernel.TimeManager();
time_manager.UnscheduleTimeEvent(timer);
}
// Remove from the condition variable. // Remove from the condition variable.
{ {
KScopedSchedulerLock sl(kernel); KScopedSchedulerLock sl(kernel);
if (Thread* owner = cur_thread->GetLockOwner(); owner != nullptr) { if (KThread* owner = cur_thread->GetLockOwner(); owner != nullptr) {
owner->RemoveWaiter(cur_thread); owner->RemoveWaiter(cur_thread);
} }

View file

@ -8,8 +8,8 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "core/hle/kernel/k_scheduler.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/kernel.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/result.h" #include "core/hle/result.h"
namespace Core { namespace Core {
@ -20,7 +20,7 @@ namespace Kernel {
class KConditionVariable { class KConditionVariable {
public: public:
using ThreadTree = typename Thread::ConditionVariableThreadTreeType; using ThreadTree = typename KThread::ConditionVariableThreadTreeType;
explicit KConditionVariable(Core::System& system_); explicit KConditionVariable(Core::System& system_);
~KConditionVariable(); ~KConditionVariable();
@ -34,7 +34,7 @@ public:
[[nodiscard]] ResultCode Wait(VAddr addr, u64 key, u32 value, s64 timeout); [[nodiscard]] ResultCode Wait(VAddr addr, u64 key, u32 value, s64 timeout);
private: private:
[[nodiscard]] Thread* SignalImpl(Thread* thread); [[nodiscard]] KThread* SignalImpl(KThread* thread);
ThreadTree thread_tree; ThreadTree thread_tree;
@ -43,14 +43,14 @@ private:
}; };
inline void BeforeUpdatePriority(const KernelCore& kernel, KConditionVariable::ThreadTree* tree, inline void BeforeUpdatePriority(const KernelCore& kernel, KConditionVariable::ThreadTree* tree,
Thread* thread) { KThread* thread) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked()); ASSERT(kernel.GlobalSchedulerContext().IsLocked());
tree->erase(tree->iterator_to(*thread)); tree->erase(tree->iterator_to(*thread));
} }
inline void AfterUpdatePriority(const KernelCore& kernel, KConditionVariable::ThreadTree* tree, inline void AfterUpdatePriority(const KernelCore& kernel, KConditionVariable::ThreadTree* tree,
Thread* thread) { KThread* thread) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked()); ASSERT(kernel.GlobalSchedulerContext().IsLocked());
tree->insert(*thread); tree->insert(*thread);

View file

@ -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

View file

@ -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

View file

@ -18,7 +18,7 @@
namespace Kernel { namespace Kernel {
class Thread; class KThread;
template <typename T> template <typename T>
concept KPriorityQueueAffinityMask = !std::is_reference_v<T> && requires(T & 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); 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(), return this->scheduled_queue.MoveToBack(member->GetPriority(), member->GetActiveCore(),
member); member);
} }

View file

@ -17,25 +17,30 @@
#include "core/cpu_manager.h" #include "core/cpu_manager.h"
#include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.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/kernel.h"
#include "core/hle/kernel/physical_core.h" #include "core/hle/kernel/physical_core.h"
#include "core/hle/kernel/process.h" #include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/time_manager.h" #include "core/hle/kernel/time_manager.h"
namespace Kernel { namespace Kernel {
static void IncrementScheduledCount(Kernel::Thread* thread) { static void IncrementScheduledCount(Kernel::KThread* thread) {
if (auto process = thread->GetOwnerProcess(); process) { if (auto process = thread->GetOwnerProcess(); process) {
process->IncrementScheduledCount(); process->IncrementScheduledCount();
} }
} }
void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule, void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule) {
Core::EmuThreadHandle global_thread) { auto scheduler = kernel.CurrentScheduler();
const u32 current_core = global_thread.host_handle;
bool must_context_switch = global_thread.guest_handle != InvalidHandle && u32 current_core{0xF};
(current_core < Core::Hardware::NUM_CPU_CORES); 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) { while (cores_pending_reschedule != 0) {
const auto core = static_cast<u32>(std::countr_zero(cores_pending_reschedule)); 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}; 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) { prev_highest_thread != highest_thread) {
if (prev_highest_thread != nullptr) { if (prev_highest_thread != nullptr) {
IncrementScheduledCount(prev_highest_thread); IncrementScheduledCount(prev_highest_thread);
prev_highest_thread->SetLastScheduledTick(system.CoreTiming().GetCPUTicks()); prev_highest_thread->SetLastScheduledTick(system.CoreTiming().GetCPUTicks());
} }
if (this->state.should_count_idle) { if (state.should_count_idle) {
if (highest_thread != nullptr) { if (highest_thread != nullptr) {
// if (Process* process = highest_thread->GetOwnerProcess(); process != nullptr) { if (Process* process = highest_thread->GetOwnerProcess(); process != nullptr) {
// process->SetRunningThread(this->core_id, highest_thread, process->SetRunningThread(core_id, highest_thread, state.idle_count);
// this->state.idle_count); }
//}
} else { } else {
this->state.idle_count++; state.idle_count++;
} }
} }
this->state.highest_priority_thread = highest_thread; state.highest_priority_thread = highest_thread;
this->state.needs_scheduling = true; state.needs_scheduling.store(true);
return (1ULL << this->core_id); return (1ULL << core_id);
} else { } else {
return 0; return 0;
} }
@ -90,16 +94,29 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
ClearSchedulerUpdateNeeded(kernel); ClearSchedulerUpdateNeeded(kernel);
u64 cores_needing_scheduling = 0, idle_cores = 0; 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); auto& priority_queue = GetPriorityQueue(kernel);
/// We want to go over all cores, finding the highest priority thread and determining if /// We want to go over all cores, finding the highest priority thread and determining if
/// scheduling is needed for that core. /// scheduling is needed for that core.
for (size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { 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 (top_thread != nullptr) {
// If the thread has no waiters, we need to check if the process has a thread pinned. // 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 { } else {
idle_cores |= (1ULL << core_id); 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. // Idle cores are bad. We're going to try to migrate threads to each idle core in turn.
while (idle_cores != 0) { while (idle_cores != 0) {
const auto core_id = static_cast<u32>(std::countr_zero(idle_cores)); 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]; s32 migration_candidates[Core::Hardware::NUM_CPU_CORES];
size_t num_candidates = 0; size_t num_candidates = 0;
@ -120,7 +137,7 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
while (suggested != nullptr) { while (suggested != nullptr) {
// Check if the suggested thread is the top thread on its core. // Check if the suggested thread is the top thread on its core.
const s32 suggested_core = suggested->GetActiveCore(); const s32 suggested_core = suggested->GetActiveCore();
if (Thread* top_thread = if (KThread* top_thread =
(suggested_core >= 0) ? top_threads[suggested_core] : nullptr; (suggested_core >= 0) ? top_threads[suggested_core] : nullptr;
top_thread != suggested) { top_thread != suggested) {
// Make sure we're not dealing with threads too high priority for migration. // 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. // Check if there's some other thread that can run on the candidate core.
const s32 candidate_core = migration_candidates[i]; const s32 candidate_core = migration_candidates[i];
suggested = top_threads[candidate_core]; suggested = top_threads[candidate_core];
if (Thread* next_on_candidate_core = if (KThread* next_on_candidate_core =
priority_queue.GetScheduledNext(candidate_core, suggested); priority_queue.GetScheduledNext(candidate_core, suggested);
next_on_candidate_core != nullptr) { next_on_candidate_core != nullptr) {
// The candidate core can run some other thread! We'll migrate its current // 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; 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()); ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// Check if the state has changed, because if it hasn't there's nothing to do. // 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()); ASSERT(kernel.GlobalSchedulerContext().IsLocked());
// If the thread is runnable, we want to change its priority in the queue. // 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) { const KAffinityMask& old_affinity, s32 old_core) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked()); ASSERT(kernel.GlobalSchedulerContext().IsLocked());
@ -237,8 +267,8 @@ void KScheduler::RotateScheduledQueue(s32 core_id, s32 priority) {
auto& priority_queue = GetPriorityQueue(kernel); auto& priority_queue = GetPriorityQueue(kernel);
// Rotate the front of the queue to the end. // Rotate the front of the queue to the end.
Thread* top_thread = priority_queue.GetScheduledFront(core_id, priority); KThread* top_thread = priority_queue.GetScheduledFront(core_id, priority);
Thread* next_thread = nullptr; KThread* next_thread = nullptr;
if (top_thread != nullptr) { if (top_thread != nullptr) {
next_thread = priority_queue.MoveToScheduledBack(top_thread); next_thread = priority_queue.MoveToScheduledBack(top_thread);
if (next_thread != 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! // 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) { while (suggested != nullptr) {
// Check if the suggested thread is the top thread on its core. // Check if the suggested thread is the top thread on its core.
const s32 suggested_core = suggested->GetActiveCore(); 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) (suggested_core >= 0) ? priority_queue.GetScheduledFront(suggested_core)
: nullptr; : nullptr;
top_on_suggested_core != suggested) { 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. // 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()) { if (best_thread == GetCurrentThread()) {
best_thread = priority_queue.GetScheduledNext(core_id, best_thread); 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 // If the best thread we can choose has a priority the same or worse than ours, try to
// migrate a higher priority thread. // migrate a higher priority thread.
if (best_thread != nullptr && best_thread->GetPriority() >= priority) { 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) { while (suggested != nullptr) {
// If the suggestion's priority is the same as ours, don't bother. // If the suggestion's priority is the same as ours, don't bother.
if (suggested->GetPriority() >= best_thread->GetPriority()) { 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. // Check if the suggested thread is the top thread on its core.
const s32 suggested_core = suggested->GetActiveCore(); 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) (suggested_core >= 0) ? priority_queue.GetScheduledFront(suggested_core)
: nullptr; : nullptr;
top_on_suggested_core != suggested) { top_on_suggested_core != suggested) {
@ -352,12 +382,14 @@ void KScheduler::DisableScheduling(KernelCore& kernel) {
} }
} }
void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling, void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) {
Core::EmuThreadHandle global_thread) {
if (auto* scheduler = kernel.CurrentScheduler(); scheduler) { if (auto* scheduler = kernel.CurrentScheduler(); scheduler) {
ASSERT(scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1);
if (scheduler->GetCurrentThread()->GetDisableDispatchCount() >= 1) {
scheduler->GetCurrentThread()->EnableDispatch(); scheduler->GetCurrentThread()->EnableDispatch();
} }
RescheduleCores(kernel, cores_needing_scheduling, global_thread); }
RescheduleCores(kernel, cores_needing_scheduling);
} }
u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) { u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) {
@ -372,15 +404,13 @@ KSchedulerPriorityQueue& KScheduler::GetPriorityQueue(KernelCore& kernel) {
return kernel.GlobalSchedulerContext().priority_queue; return kernel.GlobalSchedulerContext().priority_queue;
} }
void KScheduler::YieldWithoutCoreMigration() { void KScheduler::YieldWithoutCoreMigration(KernelCore& kernel) {
auto& kernel = system.Kernel();
// Validate preconditions. // Validate preconditions.
ASSERT(CanSchedule(kernel)); ASSERT(CanSchedule(kernel));
ASSERT(kernel.CurrentProcess() != nullptr); ASSERT(kernel.CurrentProcess() != nullptr);
// Get the current thread and process. // Get the current thread and process.
Thread& cur_thread = *GetCurrentThread(); KThread& cur_thread = Kernel::GetCurrentThread(kernel);
Process& cur_process = *kernel.CurrentProcess(); Process& cur_process = *kernel.CurrentProcess();
// If the thread's yield count matches, there's nothing for us to do. // 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(); const auto cur_state = cur_thread.GetRawState();
if (cur_state == ThreadState::Runnable) { if (cur_state == ThreadState::Runnable) {
// Put the current thread at the back of the queue. // 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)); IncrementScheduledCount(std::addressof(cur_thread));
// If the next thread is different, we have an update to perform. // If the next thread is different, we have an update to perform.
@ -413,15 +443,13 @@ void KScheduler::YieldWithoutCoreMigration() {
} }
} }
void KScheduler::YieldWithCoreMigration() { void KScheduler::YieldWithCoreMigration(KernelCore& kernel) {
auto& kernel = system.Kernel();
// Validate preconditions. // Validate preconditions.
ASSERT(CanSchedule(kernel)); ASSERT(CanSchedule(kernel));
ASSERT(kernel.CurrentProcess() != nullptr); ASSERT(kernel.CurrentProcess() != nullptr);
// Get the current thread and process. // Get the current thread and process.
Thread& cur_thread = *GetCurrentThread(); KThread& cur_thread = Kernel::GetCurrentThread(kernel);
Process& cur_process = *kernel.CurrentProcess(); Process& cur_process = *kernel.CurrentProcess();
// If the thread's yield count matches, there's nothing for us to do. // 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(); const s32 core_id = cur_thread.GetActiveCore();
// Put the current thread at the back of the queue. // 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)); IncrementScheduledCount(std::addressof(cur_thread));
// While we have a suggested thread, try to migrate it! // While we have a suggested thread, try to migrate it!
bool recheck = false; bool recheck = false;
Thread* suggested = priority_queue.GetSuggestedFront(core_id); KThread* suggested = priority_queue.GetSuggestedFront(core_id);
while (suggested != nullptr) { while (suggested != nullptr) {
// Check if the suggested thread is the thread running on its core. // Check if the suggested thread is the thread running on its core.
const s32 suggested_core = suggested->GetActiveCore(); const s32 suggested_core = suggested->GetActiveCore();
if (Thread* running_on_suggested_core = if (KThread* running_on_suggested_core =
(suggested_core >= 0) (suggested_core >= 0)
? kernel.Scheduler(suggested_core).state.highest_priority_thread ? kernel.Scheduler(suggested_core).state.highest_priority_thread
: nullptr; : nullptr;
@ -503,15 +531,13 @@ void KScheduler::YieldWithCoreMigration() {
} }
} }
void KScheduler::YieldToAnyThread() { void KScheduler::YieldToAnyThread(KernelCore& kernel) {
auto& kernel = system.Kernel();
// Validate preconditions. // Validate preconditions.
ASSERT(CanSchedule(kernel)); ASSERT(CanSchedule(kernel));
ASSERT(kernel.CurrentProcess() != nullptr); ASSERT(kernel.CurrentProcess() != nullptr);
// Get the current thread and process. // Get the current thread and process.
Thread& cur_thread = *GetCurrentThread(); KThread& cur_thread = Kernel::GetCurrentThread(kernel);
Process& cur_process = *kernel.CurrentProcess(); Process& cur_process = *kernel.CurrentProcess();
// If the thread's yield count matches, there's nothing for us to do. // 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 there's nothing scheduled, we can try to perform a migration.
if (priority_queue.GetScheduledFront(core_id) == nullptr) { if (priority_queue.GetScheduledFront(core_id) == nullptr) {
// While we have a suggested thread, try to migrate it! // 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) { while (suggested != nullptr) {
// Check if the suggested thread is the top thread on its core. // Check if the suggested thread is the top thread on its core.
const s32 suggested_core = suggested->GetActiveCore(); 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) (suggested_core >= 0) ? priority_queue.GetScheduledFront(suggested_core)
: nullptr; : nullptr;
top_on_suggested_core != suggested) { top_on_suggested_core != suggested) {
@ -581,22 +607,21 @@ void KScheduler::YieldToAnyThread() {
} }
} }
KScheduler::KScheduler(Core::System& system, std::size_t core_id) KScheduler::KScheduler(Core::System& system, s32 core_id) : system(system), core_id(core_id) {
: system(system), core_id(core_id) {
switch_fiber = std::make_shared<Common::Fiber>(OnSwitch, this); switch_fiber = std::make_shared<Common::Fiber>(OnSwitch, this);
this->state.needs_scheduling = true; state.needs_scheduling.store(true);
this->state.interrupt_task_thread_runnable = false; state.interrupt_task_thread_runnable = false;
this->state.should_count_idle = false; state.should_count_idle = false;
this->state.idle_count = 0; state.idle_count = 0;
this->state.idle_thread_stack = nullptr; state.idle_thread_stack = nullptr;
this->state.highest_priority_thread = nullptr; state.highest_priority_thread = nullptr;
} }
KScheduler::~KScheduler() = default; KScheduler::~KScheduler() = default;
Thread* KScheduler::GetCurrentThread() const { KThread* KScheduler::GetCurrentThread() const {
if (current_thread) { if (auto result = current_thread.load(); result) {
return current_thread; return result;
} }
return idle_thread; return idle_thread;
} }
@ -613,7 +638,7 @@ void KScheduler::RescheduleCurrentCore() {
phys_core.ClearInterrupt(); phys_core.ClearInterrupt();
} }
guard.lock(); guard.lock();
if (this->state.needs_scheduling) { if (state.needs_scheduling.load()) {
Schedule(); Schedule();
} else { } else {
guard.unlock(); guard.unlock();
@ -624,38 +649,41 @@ void KScheduler::OnThreadStart() {
SwitchContextStep2(); 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) { if (thread) {
thread->SetIsRunning(false); if (thread->IsCallingSvc()) {
if (thread->IsContinuousOnSVC() && !thread->IsHLEThread()) {
system.ArmInterface(core_id).ExceptionalExit(); 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); Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
cpu_core.SaveContext(thread->GetContext32()); cpu_core.SaveContext(thread->GetContext32());
cpu_core.SaveContext(thread->GetContext64()); cpu_core.SaveContext(thread->GetContext64());
// Save the TPIDR_EL0 system register in case it was modified. // Save the TPIDR_EL0 system register in case it was modified.
thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0()); thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
cpu_core.ClearExclusiveState(); cpu_core.ClearExclusiveState();
} else {
prev_thread = nullptr;
} }
thread->context_guard.unlock(); 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) { if (thread) {
ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable."); 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(); auto* const thread_owner_process = thread->GetOwnerProcess();
if (thread_owner_process != nullptr) { if (thread_owner_process != nullptr) {
system.Kernel().MakeCurrentProcess(thread_owner_process); system.Kernel().MakeCurrentProcess(thread_owner_process);
} }
if (!thread->IsHLEThread()) {
Core::ARM_Interface& cpu_core = system.ArmInterface(core_id); Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
cpu_core.LoadContext(thread->GetContext32()); cpu_core.LoadContext(thread->GetContext32());
cpu_core.LoadContext(thread->GetContext64()); cpu_core.LoadContext(thread->GetContext64());
@ -663,27 +691,34 @@ void KScheduler::Reload(Thread* thread) {
cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0()); cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0());
cpu_core.ClearExclusiveState(); cpu_core.ClearExclusiveState();
} }
}
} }
void KScheduler::SwitchContextStep2() { void KScheduler::SwitchContextStep2() {
// Load context of new thread // Load context of new thread
Reload(current_thread); Reload(current_thread.load());
RescheduleCurrentCore(); RescheduleCurrentCore();
} }
void KScheduler::ScheduleImpl() { void KScheduler::ScheduleImpl() {
Thread* previous_thread = current_thread; KThread* previous_thread = current_thread.load();
current_thread = state.highest_priority_thread; KThread* next_thread = state.highest_priority_thread;
this->state.needs_scheduling = false; state.needs_scheduling = false;
if (current_thread == previous_thread) { // 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 we're not actually switching thread, there's nothing to do.
if (next_thread == current_thread.load()) {
guard.unlock(); guard.unlock();
return; return;
} }
current_thread.store(next_thread);
Process* const previous_process = system.Kernel().CurrentProcess(); Process* const previous_process = system.Kernel().CurrentProcess();
UpdateLastContextSwitchTime(previous_thread, previous_process); UpdateLastContextSwitchTime(previous_thread, previous_process);
@ -714,28 +749,29 @@ void KScheduler::SwitchToCurrent() {
while (true) { while (true) {
{ {
std::scoped_lock lock{guard}; std::scoped_lock lock{guard};
current_thread = state.highest_priority_thread; current_thread.store(state.highest_priority_thread);
this->state.needs_scheduling = false; state.needs_scheduling.store(false);
} }
const auto is_switch_pending = [this] { const auto is_switch_pending = [this] {
std::scoped_lock lock{guard}; std::scoped_lock lock{guard};
return state.needs_scheduling.load(std::memory_order_relaxed); return state.needs_scheduling.load();
}; };
do { do {
if (current_thread != nullptr && !current_thread->IsHLEThread()) { auto next_thread = current_thread.load();
current_thread->context_guard.lock(); if (next_thread != nullptr) {
if (current_thread->GetRawState() != ThreadState::Runnable) { next_thread->context_guard.lock();
current_thread->context_guard.unlock(); if (next_thread->GetRawState() != ThreadState::Runnable) {
next_thread->context_guard.unlock();
break; break;
} }
if (static_cast<u32>(current_thread->GetProcessorID()) != core_id) { if (next_thread->GetActiveCore() != core_id) {
current_thread->context_guard.unlock(); next_thread->context_guard.unlock();
break; break;
} }
} }
std::shared_ptr<Common::Fiber>* next_context; std::shared_ptr<Common::Fiber>* next_context;
if (current_thread != nullptr) { if (next_thread != nullptr) {
next_context = &current_thread->GetHostContext(); next_context = &next_thread->GetHostContext();
} else { } else {
next_context = &idle_thread->GetHostContext(); 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 prev_switch_ticks = last_context_switch_time;
const u64 most_recent_switch_ticks = system.CoreTiming().GetCPUTicks(); const u64 most_recent_switch_ticks = system.CoreTiming().GetCPUTicks();
const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks; const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
if (thread != nullptr) { if (thread != nullptr) {
thread->UpdateCPUTimeTicks(update_ticks); thread->AddCpuTime(core_id, update_ticks);
} }
if (process != nullptr) { if (process != nullptr) {
@ -764,15 +800,10 @@ void KScheduler::Initialize() {
std::string name = "Idle Thread Id:" + std::to_string(core_id); std::string name = "Idle Thread Id:" + std::to_string(core_id);
std::function<void(void*)> init_func = Core::CpuManager::GetIdleThreadStartFunc(); std::function<void(void*)> init_func = Core::CpuManager::GetIdleThreadStartFunc();
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
ThreadType type = static_cast<ThreadType>(THREADTYPE_KERNEL | THREADTYPE_HLE | THREADTYPE_IDLE); auto thread_res = KThread::Create(system, ThreadType::Main, name, 0,
auto thread_res = Thread::Create(system, type, name, 0, 64, 0, static_cast<u32>(core_id), 0, KThread::IdleThreadPriority, 0, static_cast<u32>(core_id), 0,
nullptr, std::move(init_func), init_func_parameter); nullptr, std::move(init_func), init_func_parameter);
idle_thread = thread_res.Unwrap().get(); idle_thread = thread_res.Unwrap().get();
{
KScopedSchedulerLock lock{system.Kernel()};
idle_thread->SetState(ThreadState::Runnable);
}
} }
KScopedSchedulerLock::KScopedSchedulerLock(KernelCore& kernel) KScopedSchedulerLock::KScopedSchedulerLock(KernelCore& kernel)

View file

@ -29,29 +29,33 @@ namespace Kernel {
class KernelCore; class KernelCore;
class Process; class Process;
class SchedulerLock; class SchedulerLock;
class Thread; class KThread;
class KScheduler final { class KScheduler final {
public: public:
explicit KScheduler(Core::System& system, std::size_t core_id); explicit KScheduler(Core::System& system, s32 core_id);
~KScheduler(); ~KScheduler();
/// Reschedules to the next available thread (call after current thread is suspended) /// Reschedules to the next available thread (call after current thread is suspended)
void RescheduleCurrentCore(); void RescheduleCurrentCore();
/// Reschedules cores pending reschedule, to be called on EnableScheduling. /// Reschedules cores pending reschedule, to be called on EnableScheduling.
static void RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule, static void RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule);
Core::EmuThreadHandle global_thread);
/// The next two are for SingleCore Only. /// The next two are for SingleCore Only.
/// Unload current thread before preempting core. /// Unload current thread before preempting core.
void Unload(Thread* thread); void Unload(KThread* thread);
/// Reload current thread after core preemption. /// Reload current thread after core preemption.
void Reload(Thread* thread); void Reload(KThread* thread);
/// Gets the current running 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. /// Gets the timestamp for the last context switch in ticks.
[[nodiscard]] u64 GetLastContextSwitchTicks() const; [[nodiscard]] u64 GetLastContextSwitchTicks() const;
@ -72,14 +76,14 @@ public:
return switch_fiber; 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. * 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. * @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. * 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. * @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. * 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. * @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. /// 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. /// 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. /// 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); const KAffinityMask& old_affinity, s32 old_core);
static bool CanSchedule(KernelCore& kernel); static bool CanSchedule(KernelCore& kernel);
@ -114,8 +120,7 @@ public:
static void SetSchedulerUpdateNeeded(KernelCore& kernel); static void SetSchedulerUpdateNeeded(KernelCore& kernel);
static void ClearSchedulerUpdateNeeded(KernelCore& kernel); static void ClearSchedulerUpdateNeeded(KernelCore& kernel);
static void DisableScheduling(KernelCore& kernel); static void DisableScheduling(KernelCore& kernel);
static void EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling, static void EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling);
Core::EmuThreadHandle global_thread);
[[nodiscard]] static u64 UpdateHighestPriorityThreads(KernelCore& kernel); [[nodiscard]] static u64 UpdateHighestPriorityThreads(KernelCore& kernel);
private: private:
@ -163,13 +168,15 @@ private:
* most recent tick count retrieved. No special arithmetic is * most recent tick count retrieved. No special arithmetic is
* applied to it. * applied to it.
*/ */
void UpdateLastContextSwitchTime(Thread* thread, Process* process); void UpdateLastContextSwitchTime(KThread* thread, Process* process);
static void OnSwitch(void* this_scheduler); static void OnSwitch(void* this_scheduler);
void SwitchToCurrent(); void SwitchToCurrent();
Thread* current_thread{}; KThread* prev_thread{};
Thread* idle_thread{}; std::atomic<KThread*> current_thread{};
KThread* idle_thread;
std::shared_ptr<Common::Fiber> switch_fiber{}; std::shared_ptr<Common::Fiber> switch_fiber{};
@ -178,7 +185,7 @@ private:
bool interrupt_task_thread_runnable{}; bool interrupt_task_thread_runnable{};
bool should_count_idle{}; bool should_count_idle{};
u64 idle_count{}; u64 idle_count{};
Thread* highest_priority_thread{}; KThread* highest_priority_thread{};
void* idle_thread_stack{}; void* idle_thread_stack{};
}; };
@ -186,7 +193,7 @@ private:
Core::System& system; Core::System& system;
u64 last_context_switch_time{}; u64 last_context_switch_time{};
const std::size_t core_id; const s32 core_id;
Common::SpinLock guard{}; Common::SpinLock guard{};
}; };

View file

@ -10,6 +10,7 @@
#include "common/assert.h" #include "common/assert.h"
#include "common/spin_lock.h" #include "common/spin_lock.h"
#include "core/hardware_properties.h" #include "core/hardware_properties.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
namespace Kernel { namespace Kernel {
@ -22,46 +23,45 @@ public:
explicit KAbstractSchedulerLock(KernelCore& kernel_) : kernel{kernel_} {} explicit KAbstractSchedulerLock(KernelCore& kernel_) : kernel{kernel_} {}
bool IsLockedByCurrentThread() const { bool IsLockedByCurrentThread() const {
return this->owner_thread == kernel.GetCurrentEmuThreadID(); return owner_thread == GetCurrentThreadPointer(kernel);
} }
void Lock() { void Lock() {
if (this->IsLockedByCurrentThread()) { if (IsLockedByCurrentThread()) {
// If we already own the lock, we can just increment the count. // If we already own the lock, we can just increment the count.
ASSERT(this->lock_count > 0); ASSERT(lock_count > 0);
this->lock_count++; lock_count++;
} else { } else {
// Otherwise, we want to disable scheduling and acquire the spinlock. // Otherwise, we want to disable scheduling and acquire the spinlock.
SchedulerType::DisableScheduling(kernel); SchedulerType::DisableScheduling(kernel);
this->spin_lock.lock(); spin_lock.lock();
// For debug, ensure that our state is valid. // For debug, ensure that our state is valid.
ASSERT(this->lock_count == 0); ASSERT(lock_count == 0);
ASSERT(this->owner_thread == Core::EmuThreadHandle::InvalidHandle()); ASSERT(owner_thread == nullptr);
// Increment count, take ownership. // Increment count, take ownership.
this->lock_count = 1; lock_count = 1;
this->owner_thread = kernel.GetCurrentEmuThreadID(); owner_thread = GetCurrentThreadPointer(kernel);
} }
} }
void Unlock() { void Unlock() {
ASSERT(this->IsLockedByCurrentThread()); ASSERT(IsLockedByCurrentThread());
ASSERT(this->lock_count > 0); ASSERT(lock_count > 0);
// Release an instance of the lock. // 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. // We're no longer going to hold the lock. Take note of what cores need scheduling.
const u64 cores_needing_scheduling = const u64 cores_needing_scheduling =
SchedulerType::UpdateHighestPriorityThreads(kernel); SchedulerType::UpdateHighestPriorityThreads(kernel);
Core::EmuThreadHandle leaving_thread = owner_thread;
// Note that we no longer hold the lock, and unlock the spinlock. // Note that we no longer hold the lock, and unlock the spinlock.
this->owner_thread = Core::EmuThreadHandle::InvalidHandle(); owner_thread = nullptr;
this->spin_lock.unlock(); spin_lock.unlock();
// Enable scheduling, and perform a rescheduling operation. // 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; KernelCore& kernel;
Common::SpinLock spin_lock{}; Common::SpinLock spin_lock{};
s32 lock_count{}; s32 lock_count{};
Core::EmuThreadHandle owner_thread{Core::EmuThreadHandle::InvalidHandle()}; KThread* owner_thread{};
}; };
} // namespace Kernel } // namespace Kernel

View file

@ -9,27 +9,24 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "core/hle/kernel/handle_table.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/kernel.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/time_manager.h" #include "core/hle/kernel/time_manager.h"
namespace Kernel { namespace Kernel {
class KScopedSchedulerLockAndSleep { class KScopedSchedulerLockAndSleep {
public: public:
explicit KScopedSchedulerLockAndSleep(KernelCore& kernel, Handle& event_handle, Thread* t, explicit KScopedSchedulerLockAndSleep(KernelCore& kernel, KThread* t, s64 timeout)
s64 timeout) : kernel(kernel), thread(t), timeout_tick(timeout) {
: kernel(kernel), event_handle(event_handle), thread(t), timeout_tick(timeout) {
event_handle = InvalidHandle;
// Lock the scheduler. // Lock the scheduler.
kernel.GlobalSchedulerContext().scheduler_lock.Lock(); kernel.GlobalSchedulerContext().scheduler_lock.Lock();
} }
~KScopedSchedulerLockAndSleep() { ~KScopedSchedulerLockAndSleep() {
// Register the sleep. // Register the sleep.
if (this->timeout_tick > 0) { if (timeout_tick > 0) {
kernel.TimeManager().ScheduleTimeEvent(event_handle, this->thread, this->timeout_tick); kernel.TimeManager().ScheduleTimeEvent(thread, timeout_tick);
} }
// Unlock the scheduler. // Unlock the scheduler.
@ -37,13 +34,12 @@ public:
} }
void CancelSleep() { void CancelSleep() {
this->timeout_tick = 0; timeout_tick = 0;
} }
private: private:
KernelCore& kernel; KernelCore& kernel;
Handle& event_handle; KThread* thread{};
Thread* thread{};
s64 timeout_tick{}; s64 timeout_tick{};
}; };

View file

@ -7,9 +7,9 @@
#include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.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_synchronization_object.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/svc_results.h" #include "core/hle/kernel/svc_results.h"
#include "core/hle/kernel/thread.h"
namespace Kernel { namespace Kernel {
@ -20,12 +20,11 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index,
std::vector<ThreadListNode> thread_nodes(num_objects); std::vector<ThreadListNode> thread_nodes(num_objects);
// Prepare for wait. // Prepare for wait.
Thread* thread = kernel.CurrentScheduler()->GetCurrentThread(); KThread* thread = kernel.CurrentScheduler()->GetCurrentThread();
Handle timer = InvalidHandle;
{ {
// Setup the scheduling lock and sleep. // 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. // Check if any of the objects are already signaled.
for (auto i = 0; i < num_objects; ++i) { for (auto i = 0; i < num_objects; ++i) {
@ -90,10 +89,7 @@ ResultCode KSynchronizationObject::Wait(KernelCore& kernel, s32* out_index,
thread->SetWaitObjectsForDebugging({}); thread->SetWaitObjectsForDebugging({});
// Cancel the timer as needed. // Cancel the timer as needed.
if (timer != InvalidHandle) { kernel.TimeManager().UnscheduleTimeEvent(thread);
auto& time_manager = kernel.TimeManager();
time_manager.UnscheduleTimeEvent(timer);
}
// Get the wait result. // Get the wait result.
ResultCode wait_result{RESULT_SUCCESS}; 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(KernelCore& kernel) : Object{kernel} {}
KSynchronizationObject ::~KSynchronizationObject() = default; KSynchronizationObject::~KSynchronizationObject() = default;
void KSynchronizationObject::NotifyAvailable(ResultCode result) { void KSynchronizationObject::NotifyAvailable(ResultCode result) {
KScopedSchedulerLock lock(kernel); KScopedSchedulerLock lock(kernel);
@ -148,7 +144,7 @@ void KSynchronizationObject::NotifyAvailable(ResultCode result) {
// Iterate over each thread. // Iterate over each thread.
for (auto* cur_node = thread_list_head; cur_node != nullptr; cur_node = cur_node->next) { 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) { if (thread->GetState() == ThreadState::Waiting) {
thread->SetSyncedObject(this, result); thread->SetSyncedObject(this, result);
thread->SetState(ThreadState::Runnable); thread->SetState(ThreadState::Runnable);
@ -156,8 +152,8 @@ void KSynchronizationObject::NotifyAvailable(ResultCode result) {
} }
} }
std::vector<Thread*> KSynchronizationObject::GetWaitingThreadsForDebugging() const { std::vector<KThread*> KSynchronizationObject::GetWaitingThreadsForDebugging() const {
std::vector<Thread*> threads; std::vector<KThread*> threads;
// If debugging, dump the list of waiters. // If debugging, dump the list of waiters.
{ {

View file

@ -13,14 +13,14 @@ namespace Kernel {
class KernelCore; class KernelCore;
class Synchronization; class Synchronization;
class Thread; class KThread;
/// Class that represents a Kernel object that a thread can be waiting on /// Class that represents a Kernel object that a thread can be waiting on
class KSynchronizationObject : public Object { class KSynchronizationObject : public Object {
public: public:
struct ThreadListNode { struct ThreadListNode {
ThreadListNode* next{}; ThreadListNode* next{};
Thread* thread{}; KThread* thread{};
}; };
[[nodiscard]] static ResultCode Wait(KernelCore& kernel, s32* out_index, [[nodiscard]] static ResultCode Wait(KernelCore& kernel, s32* out_index,
@ -29,7 +29,7 @@ public:
[[nodiscard]] virtual bool IsSignaled() const = 0; [[nodiscard]] virtual bool IsSignaled() const = 0;
[[nodiscard]] std::vector<Thread*> GetWaitingThreadsForDebugging() const; [[nodiscard]] std::vector<KThread*> GetWaitingThreadsForDebugging() const;
protected: protected:
explicit KSynchronizationObject(KernelCore& kernel); explicit KSynchronizationObject(KernelCore& kernel);

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -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

View file

@ -29,6 +29,7 @@
#include "core/hle/kernel/errors.h" #include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/k_scheduler.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/kernel.h"
#include "core/hle/kernel/memory/memory_layout.h" #include "core/hle/kernel/memory/memory_layout.h"
#include "core/hle/kernel/memory/memory_manager.h" #include "core/hle/kernel/memory/memory_manager.h"
@ -38,7 +39,6 @@
#include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/service_thread.h" #include "core/hle/kernel/service_thread.h"
#include "core/hle/kernel/shared_memory.h" #include "core/hle/kernel/shared_memory.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/time_manager.h" #include "core/hle/kernel/time_manager.h"
#include "core/hle/lock.h" #include "core/hle/lock.h"
#include "core/hle/result.h" #include "core/hle/result.h"
@ -57,11 +57,13 @@ struct KernelCore::Impl {
} }
void Initialize(KernelCore& kernel) { void Initialize(KernelCore& kernel) {
global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
RegisterHostThread(); RegisterHostThread();
global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
service_thread_manager = service_thread_manager =
std::make_unique<Common::ThreadWorker>(1, "yuzu:ServiceThreadManager"); std::make_unique<Common::ThreadWorker>(1, "yuzu:ServiceThreadManager");
is_phantom_mode_for_singlecore = false;
InitializePhysicalCores(); InitializePhysicalCores();
InitializeSystemResourceLimit(kernel); InitializeSystemResourceLimit(kernel);
@ -116,14 +118,14 @@ struct KernelCore::Impl {
void InitializePhysicalCores() { void InitializePhysicalCores() {
exclusive_monitor = exclusive_monitor =
Core::MakeExclusiveMonitor(system.Memory(), Core::Hardware::NUM_CPU_CORES); 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); schedulers[i] = std::make_unique<Kernel::KScheduler>(system, i);
cores.emplace_back(i, system, *schedulers[i], interrupts); cores.emplace_back(i, system, *schedulers[i], interrupts);
} }
} }
void InitializeSchedulers() { 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(); cores[i].Scheduler().Initialize();
} }
} }
@ -168,11 +170,9 @@ struct KernelCore::Impl {
std::string name = "Suspend Thread Id:" + std::to_string(i); std::string name = "Suspend Thread Id:" + std::to_string(i);
std::function<void(void*)> init_func = Core::CpuManager::GetSuspendThreadStartFunc(); std::function<void(void*)> init_func = Core::CpuManager::GetSuspendThreadStartFunc();
void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater(); void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
const auto type = auto thread_res = KThread::Create(system, ThreadType::HighPriority, std::move(name), 0,
static_cast<ThreadType>(THREADTYPE_KERNEL | THREADTYPE_HLE | THREADTYPE_SUSPEND); 0, 0, static_cast<u32>(i), 0, nullptr,
auto thread_res = std::move(init_func), init_func_parameter);
Thread::Create(system, type, 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(); suspend_threads[i] = std::move(thread_res).Unwrap();
} }
@ -207,6 +207,17 @@ struct KernelCore::Impl {
return host_thread_id; 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 /// Registers a CPU core thread by allocating a host thread ID for it
void RegisterCoreThread(std::size_t core_id) { void RegisterCoreThread(std::size_t core_id) {
ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); 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 /// Registers a new host thread by allocating a host thread ID for it
void RegisterHostThread() { void RegisterHostThread() {
[[maybe_unused]] const auto this_id = GetHostThreadId(); [[maybe_unused]] const auto this_id = GetHostThreadId();
[[maybe_unused]] const auto dummy_thread = GetHostDummyThread();
} }
[[nodiscard]] u32 GetCurrentHostThreadID() { [[nodiscard]] u32 GetCurrentHostThreadID() {
@ -229,20 +241,21 @@ struct KernelCore::Impl {
return this_id; return this_id;
} }
[[nodiscard]] Core::EmuThreadHandle GetCurrentEmuThreadID() { bool IsPhantomModeForSingleCore() const {
Core::EmuThreadHandle result = Core::EmuThreadHandle::InvalidHandle(); return is_phantom_mode_for_singlecore;
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(); void SetIsPhantomModeForSingleCore(bool value) {
if (current != nullptr && !current->IsPhantomMode()) { ASSERT(!is_multicore);
result.guest_handle = current->GetGlobalHandle(); is_phantom_mode_for_singlecore = value;
} else {
result.guest_handle = InvalidHandle;
} }
return result;
KThread* GetCurrentEmuThread() {
const auto thread_id = GetCurrentHostThreadID();
if (thread_id >= Core::Hardware::NUM_CPU_CORES) {
return GetHostDummyThread();
}
return schedulers[thread_id]->GetCurrentThread();
} }
void InitializeMemoryLayout() { void InitializeMemoryLayout() {
@ -342,11 +355,12 @@ struct KernelCore::Impl {
// the release of itself // the release of itself
std::unique_ptr<Common::ThreadWorker> service_thread_manager; 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<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{};
std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{}; std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
bool is_multicore{}; bool is_multicore{};
bool is_phantom_mode_for_singlecore{};
u32 single_core_thread_id{}; u32 single_core_thread_id{};
std::array<u64, Core::Hardware::NUM_CPU_CORES> svc_ticks{}; 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; return impl->system_resource_limit;
} }
std::shared_ptr<Thread> KernelCore::RetrieveThreadFromGlobalHandleTable(Handle handle) const { std::shared_ptr<KThread> KernelCore::RetrieveThreadFromGlobalHandleTable(Handle handle) const {
return impl->global_handle_table.Get<Thread>(handle); return impl->global_handle_table.Get<KThread>(handle);
} }
void KernelCore::AppendNewProcess(std::shared_ptr<Process> process) { void KernelCore::AppendNewProcess(std::shared_ptr<Process> process) {
@ -546,8 +560,8 @@ u32 KernelCore::GetCurrentHostThreadID() const {
return impl->GetCurrentHostThreadID(); return impl->GetCurrentHostThreadID();
} }
Core::EmuThreadHandle KernelCore::GetCurrentEmuThreadID() const { KThread* KernelCore::GetCurrentEmuThread() const {
return impl->GetCurrentEmuThreadID(); return impl->GetCurrentEmuThread();
} }
Memory::MemoryManager& KernelCore::MemoryManager() { 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 } // namespace Kernel

View file

@ -43,9 +43,13 @@ class KScheduler;
class SharedMemory; class SharedMemory;
class ServiceThread; class ServiceThread;
class Synchronization; class Synchronization;
class Thread; class KThread;
class TimeManager; class TimeManager;
using EmuThreadHandle = uintptr_t;
constexpr EmuThreadHandle EmuThreadHandleInvalid{};
constexpr EmuThreadHandle EmuThreadHandleReserved{1ULL << 63};
/// Represents a single instance of the kernel. /// Represents a single instance of the kernel.
class KernelCore { class KernelCore {
private: private:
@ -84,7 +88,7 @@ public:
std::shared_ptr<ResourceLimit> GetSystemResourceLimit() const; std::shared_ptr<ResourceLimit> GetSystemResourceLimit() const;
/// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table. /// 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. /// Adds the given shared pointer to an internal list of active processes.
void AppendNewProcess(std::shared_ptr<Process> process); void AppendNewProcess(std::shared_ptr<Process> process);
@ -161,8 +165,8 @@ public:
/// Determines whether or not the given port is a valid named port. /// Determines whether or not the given port is a valid named port.
bool IsValidNamedPort(NamedPortTable::const_iterator port) const; bool IsValidNamedPort(NamedPortTable::const_iterator port) const;
/// Gets the current host_thread/guest_thread handle. /// Gets the current host_thread/guest_thread pointer.
Core::EmuThreadHandle GetCurrentEmuThreadID() const; KThread* GetCurrentEmuThread() const;
/// Gets the current host_thread handle. /// Gets the current host_thread handle.
u32 GetCurrentHostThreadID() const; u32 GetCurrentHostThreadID() const;
@ -237,10 +241,14 @@ public:
*/ */
void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread); 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: private:
friend class Object; friend class Object;
friend class Process; friend class Process;
friend class Thread; friend class KThread;
/// Creates a new object ID, incrementing the internal object ID counter. /// Creates a new object ID, incrementing the internal object ID counter.
u32 CreateNewObjectID(); u32 CreateNewObjectID();

View file

@ -61,6 +61,8 @@ public:
*/ */
bool IsWaitable() const; bool IsWaitable() const;
virtual void Finalize() = 0;
protected: protected:
/// The kernel instance this object was created under. /// The kernel instance this object was created under.
KernelCore& kernel; KernelCore& kernel;

View file

@ -16,13 +16,13 @@
#include "core/hle/kernel/code_set.h" #include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/errors.h" #include "core/hle/kernel/errors.h"
#include "core/hle/kernel/k_scheduler.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/kernel.h"
#include "core/hle/kernel/memory/memory_block_manager.h" #include "core/hle/kernel/memory/memory_block_manager.h"
#include "core/hle/kernel/memory/page_table.h" #include "core/hle/kernel/memory/page_table.h"
#include "core/hle/kernel/memory/slab_heap.h" #include "core/hle/kernel/memory/slab_heap.h"
#include "core/hle/kernel/process.h" #include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/lock.h" #include "core/hle/lock.h"
#include "core/memory.h" #include "core/memory.h"
#include "core/settings.h" #include "core/settings.h"
@ -38,11 +38,10 @@ namespace {
*/ */
void SetupMainThread(Core::System& system, Process& owner_process, u32 priority, VAddr stack_top) { void SetupMainThread(Core::System& system, Process& owner_process, u32 priority, VAddr stack_top) {
const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart(); const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart();
ThreadType type = THREADTYPE_USER; auto thread_res = KThread::Create(system, ThreadType::User, "main", entry_point, priority, 0,
auto thread_res = Thread::Create(system, type, "main", entry_point, priority, 0, owner_process.GetIdealCoreId(), stack_top, &owner_process);
owner_process.GetIdealCore(), 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 // Register 1 must be a handle to the main thread
const Handle thread_handle = owner_process.GetHandleTable().Create(thread).Unwrap(); const Handle thread_handle = owner_process.GetHandleTable().Create(thread).Unwrap();
@ -137,6 +136,23 @@ std::shared_ptr<ResourceLimit> Process::GetResourceLimit() const {
return resource_limit; 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 { u64 Process::GetTotalPhysicalMemoryAvailable() const {
const u64 capacity{resource_limit->GetCurrentResourceValue(ResourceType::PhysicalMemory) + const u64 capacity{resource_limit->GetCurrentResourceValue(ResourceType::PhysicalMemory) +
page_table->GetTotalHeapSize() + GetSystemResourceSize() + image_size + page_table->GetTotalHeapSize() + GetSystemResourceSize() + image_size +
@ -162,11 +178,66 @@ u64 Process::GetTotalPhysicalMemoryUsedWithoutSystemResource() const {
return GetTotalPhysicalMemoryUsed() - GetSystemResourceUsage(); 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); thread_list.push_back(thread);
} }
void Process::UnregisterThread(const Thread* thread) { void Process::UnregisterThread(const KThread* thread) {
thread_list.remove(thread); thread_list.remove(thread);
} }
@ -267,7 +338,7 @@ void Process::Run(s32 main_thread_priority, u64 stack_size) {
void Process::PrepareForTermination() { void Process::PrepareForTermination() {
ChangeStatus(ProcessStatus::Exiting); 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) { for (auto& thread : thread_list) {
if (thread->GetOwnerProcess() != this) if (thread->GetOwnerProcess() != this)
continue; continue;
@ -279,7 +350,7 @@ void Process::PrepareForTermination() {
ASSERT_MSG(thread->GetState() == ThreadState::Waiting, ASSERT_MSG(thread->GetState() == ThreadState::Waiting,
"Exiting processes with non-waiting threads is currently unimplemented"); "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) Process::Process(Core::System& system)
: KSynchronizationObject{system.Kernel()}, : KSynchronizationObject{system.Kernel()},
page_table{std::make_unique<Memory::PageTable>(system)}, handle_table{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; Process::~Process() = default;

View file

@ -30,7 +30,7 @@ namespace Kernel {
class KernelCore; class KernelCore;
class ResourceLimit; class ResourceLimit;
class Thread; class KThread;
class TLSPage; class TLSPage;
struct CodeSet; struct CodeSet;
@ -173,10 +173,15 @@ public:
std::shared_ptr<ResourceLimit> GetResourceLimit() const; std::shared_ptr<ResourceLimit> GetResourceLimit() const;
/// Gets the ideal CPU core ID for this process /// Gets the ideal CPU core ID for this process
u8 GetIdealCore() const { u8 GetIdealCoreId() const {
return ideal_core; 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. /// Gets the bitmask of allowed cores that this process' threads can run on.
u64 GetCoreMask() const { u64 GetCoreMask() const {
return capabilities.GetCoreMask(); return capabilities.GetCoreMask();
@ -212,6 +217,14 @@ public:
return is_64bit_process; 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. /// Gets the total running time of the process instance in ticks.
u64 GetCPUTimeTicks() const { u64 GetCPUTimeTicks() const {
return total_process_running_time_ticks; return total_process_running_time_ticks;
@ -232,6 +245,33 @@ public:
++schedule_count; ++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 /// Gets 8 bytes of random data for svcGetInfo RandomEntropy
u64 GetRandomEntropy(std::size_t index) const { u64 GetRandomEntropy(std::size_t index) const {
return random_entropy.at(index); return random_entropy.at(index);
@ -252,17 +292,17 @@ public:
u64 GetTotalPhysicalMemoryUsedWithoutSystemResource() const; u64 GetTotalPhysicalMemoryUsedWithoutSystemResource() const;
/// Gets the list of all threads created with this process as their owner. /// 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; return thread_list;
} }
/// Registers a thread as being created under this process, /// Registers a thread as being created under this process,
/// adding it to this process' thread list. /// 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 /// Unregisters a thread from this process, removing it
/// from this process' thread list. /// 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. /// Clears the signaled state of the process if and only if it's signaled.
/// ///
@ -303,6 +343,15 @@ public:
bool IsSignaled() const override; bool IsSignaled() const override;
void Finalize() override {}
void PinCurrentThread();
void UnpinCurrentThread();
KLightLock& GetStateLock() {
return state_lock;
}
/////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////
// Thread-local storage management // Thread-local storage management
@ -313,6 +362,20 @@ public:
void FreeTLSRegion(VAddr tls_address); void FreeTLSRegion(VAddr tls_address);
private: 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 /// Changes the process status. If the status is different
/// from the current process status, then this will trigger /// from the current process status, then this will trigger
/// a process signal. /// a process signal.
@ -380,7 +443,7 @@ private:
std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy{}; std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy{};
/// List of threads that are running with this process as their owner. /// 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 /// Address of the top of the main thread's stack
VAddr main_thread_stack_top{}; VAddr main_thread_stack_top{};
@ -401,6 +464,19 @@ private:
s64 schedule_count{}; s64 schedule_count{};
bool is_signaled{}; 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 /// System context
Core::System& system; Core::System& system;

View file

@ -7,10 +7,10 @@
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/hle/kernel/errors.h" #include "core/hle/kernel/errors.h"
#include "core/hle/kernel/k_scheduler.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/kernel.h"
#include "core/hle/kernel/object.h" #include "core/hle/kernel/object.h"
#include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/thread.h"
namespace Kernel { namespace Kernel {

View file

@ -47,6 +47,8 @@ public:
bool IsSignaled() const override; bool IsSignaled() const override;
void Finalize() override {}
private: private:
explicit ReadableEvent(KernelCore& kernel); explicit ReadableEvent(KernelCore& kernel);

View file

@ -85,6 +85,8 @@ public:
*/ */
ResultCode SetLimitValue(ResourceType resource, s64 value); ResultCode SetLimitValue(ResourceType resource, s64 value);
void Finalize() override {}
private: private:
// TODO(Subv): Increment resource limit current values in their respective Kernel::T::Create // TODO(Subv): Increment resource limit current values in their respective Kernel::T::Create
// functions // functions

View file

@ -6,10 +6,10 @@
#include "common/assert.h" #include "common/assert.h"
#include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/errors.h" #include "core/hle/kernel/errors.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/object.h" #include "core/hle/kernel/object.h"
#include "core/hle/kernel/server_port.h" #include "core/hle/kernel/server_port.h"
#include "core/hle/kernel/server_session.h" #include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/thread.h"
namespace Kernel { namespace Kernel {

View file

@ -81,6 +81,8 @@ public:
bool IsSignaled() const override; bool IsSignaled() const override;
void Finalize() override {}
private: private:
/// ServerSessions waiting to be accepted by the port /// ServerSessions waiting to be accepted by the port
std::vector<std::shared_ptr<ServerSession>> pending_sessions; std::vector<std::shared_ptr<ServerSession>> pending_sessions;

View file

@ -15,11 +15,11 @@
#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/k_scheduler.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/kernel.h"
#include "core/hle/kernel/process.h" #include "core/hle/kernel/process.h"
#include "core/hle/kernel/server_session.h" #include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/session.h" #include "core/hle/kernel/session.h"
#include "core/hle/kernel/thread.h"
#include "core/memory.h" #include "core/memory.h"
namespace Kernel { namespace Kernel {
@ -116,7 +116,7 @@ ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& con
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
ResultCode ServerSession::QueueSyncRequest(std::shared_ptr<Thread> thread, ResultCode ServerSession::QueueSyncRequest(std::shared_ptr<KThread> thread,
Core::Memory::Memory& memory) { Core::Memory::Memory& memory) {
u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(thread->GetTLSAddress()))}; u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(thread->GetTLSAddress()))};
auto context = auto context =
@ -154,14 +154,14 @@ ResultCode ServerSession::CompleteSyncRequest(HLERequestContext& context) {
KScopedSchedulerLock lock(kernel); KScopedSchedulerLock lock(kernel);
if (!context.IsThreadWaiting()) { if (!context.IsThreadWaiting()) {
context.GetThread().Wakeup(); context.GetThread().Wakeup();
context.GetThread().SetSynchronizationResults(nullptr, result); context.GetThread().SetSyncedObject(nullptr, result);
} }
} }
return result; return result;
} }
ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread, ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<KThread> thread,
Core::Memory::Memory& memory, Core::Memory::Memory& memory,
Core::Timing::CoreTiming& core_timing) { Core::Timing::CoreTiming& core_timing) {
return QueueSyncRequest(std::move(thread), memory); return QueueSyncRequest(std::move(thread), memory);

View file

@ -29,7 +29,7 @@ class HLERequestContext;
class KernelCore; class KernelCore;
class Session; class Session;
class SessionRequestHandler; class SessionRequestHandler;
class Thread; class KThread;
/** /**
* Kernel object representing the server endpoint of an IPC session. Sessions are the basic CTR-OS * 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. * @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); Core::Timing::CoreTiming& core_timing);
/// Called when a client disconnection occurs. /// Called when a client disconnection occurs.
@ -126,9 +126,11 @@ public:
bool IsSignaled() const override; bool IsSignaled() const override;
void Finalize() override {}
private: private:
/// Queues a sync request from the emulated application. /// 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. /// Completes a sync request from the emulated application.
ResultCode CompleteSyncRequest(HLERequestContext& context); 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 /// 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. /// 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. /// 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 /// Thread whose request is currently being handled. A request is considered "handled" when a
/// response is sent via svcReplyAndReceive. /// response is sent via svcReplyAndReceive.
/// TODO(Subv): Find a better name for this. /// 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 /// When set to True, converts the session to a domain at the end of the command
bool convert_to_domain{}; bool convert_to_domain{};

View file

@ -39,6 +39,8 @@ public:
bool IsSignaled() const override; bool IsSignaled() const override;
void Finalize() override {}
std::shared_ptr<ClientSession> Client() { std::shared_ptr<ClientSession> Client() {
if (auto result{client.lock()}) { if (auto result{client.lock()}) {
return result; return result;

View file

@ -71,6 +71,8 @@ public:
return device_memory.GetPointer(physical_address + offset); return device_memory.GetPointer(physical_address + offset);
} }
void Finalize() override {}
private: private:
Core::DeviceMemory& device_memory; Core::DeviceMemory& device_memory;
Process* owner_process{}; Process* owner_process{};

View file

@ -29,6 +29,7 @@
#include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.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_synchronization_object.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/memory/memory_block.h" #include "core/hle/kernel/memory/memory_block.h"
#include "core/hle/kernel/memory/memory_layout.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_results.h"
#include "core/hle/kernel/svc_types.h" #include "core/hle/kernel/svc_types.h"
#include "core/hle/kernel/svc_wrap.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/time_manager.h"
#include "core/hle/kernel/transfer_memory.h" #include "core/hle/kernel/transfer_memory.h"
#include "core/hle/kernel/writable_event.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()); 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) { 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. /// 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); 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 auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle); const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle);
if (!thread) { R_UNLESS(thread, Svc::ResultInvalidHandle);
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", thread_handle);
return ERR_INVALID_HANDLE;
}
*thread_id = thread->GetThreadID(); // Get the thread's id.
*out_thread_id = thread->GetThreadID();
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
static ResultCode GetThreadId32(Core::System& system, u32* thread_id_low, u32* thread_id_high, static ResultCode GetThreadId32(Core::System& system, u32* out_thread_id_low,
Handle thread_handle) { u32* out_thread_id_high, Handle thread_handle) {
u64 thread_id{}; u64 out_thread_id{};
const ResultCode result{GetThreadId(system, &thread_id, thread_handle)}; const ResultCode result{GetThreadId(system, &out_thread_id, thread_handle)};
*thread_id_low = static_cast<u32>(thread_id >> 32); *out_thread_id_low = static_cast<u32>(out_thread_id >> 32);
*thread_id_high = static_cast<u32>(thread_id & std::numeric_limits<u32>::max()); *out_thread_id_high = static_cast<u32>(out_thread_id & std::numeric_limits<u32>::max());
return result; return result;
} }
@ -395,7 +395,7 @@ static ResultCode GetProcessId(Core::System& system, u64* process_id, Handle han
return RESULT_SUCCESS; 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) { if (thread) {
const Process* const owner_process = thread->GetOwnerProcess(); const Process* const owner_process = thread->GetOwnerProcess();
if (!owner_process) { 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) { static ResultCode CancelSynchronization(Core::System& system, Handle thread_handle) {
LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", 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(); const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle); std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle);
if (!thread) { R_UNLESS(thread, Svc::ResultInvalidHandle);
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
thread_handle);
return ERR_INVALID_HANDLE;
}
thread->CancelWait(); // Cancel the thread's wait.
thread->WaitCancel();
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
@ -630,7 +628,7 @@ static void Break(Core::System& system, u32 reason, u64 info1, u64 info2) {
handle_debug_buffer(info1, info2); handle_debug_buffer(info1, info2);
auto* const current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread(); 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(); 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; 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)); static_cast<Handle>(handle));
if (!thread) { if (!thread) {
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", 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(); const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTicks();
u64 out_ticks = 0; u64 out_ticks = 0;
if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) { 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); out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks);
} else if (same_thread && info_sub_id == system.CurrentCoreIndex()) { } 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); return UnmapPhysicalMemory(system, addr, size);
} }
/// Sets the thread activity constexpr bool IsValidThreadActivity(Svc::ThreadActivity thread_activity) {
static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 activity) { switch (thread_activity) {
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity); case Svc::ThreadActivity::Runnable:
if (activity > static_cast<u32>(ThreadActivity::Paused)) { case Svc::ThreadActivity::Paused:
return ERR_INVALID_ENUM_VALUE; 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;
}
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;
}
if (thread.get() == system.Kernel().CurrentScheduler()->GetCurrentThread()) {
LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread");
return ERR_BUSY;
}
return thread->SetActivity(static_cast<ThreadActivity>(activity));
} }
static ResultCode SetThreadActivity32(Core::System& system, Handle handle, u32 activity) { /// Sets the thread activity
return SetThreadActivity(system, handle, 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);
// Validate the activity.
R_UNLESS(IsValidThreadActivity(thread_activity), Svc::ResultInvalidEnumValue);
// 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 RESULT_SUCCESS;
}
static ResultCode SetThreadActivity32(Core::System& system, Handle thread_handle,
Svc::ThreadActivity thread_activity) {
return SetThreadActivity(system, thread_handle, thread_activity);
} }
/// Gets the thread context /// Gets the thread context
static ResultCode GetThreadContext(Core::System& system, VAddr thread_context, Handle handle) { static ResultCode GetThreadContext(Core::System& system, VAddr out_context, Handle thread_handle) {
LOG_DEBUG(Kernel_SVC, "called, context=0x{:08X}, thread=0x{:X}", thread_context, 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 auto* current_process = system.Kernel().CurrentProcess();
const std::shared_ptr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); const std::shared_ptr<KThread> thread =
if (!thread) { current_process->GetHandleTable().Get<KThread>(thread_handle);
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle); R_UNLESS(thread, Svc::ResultInvalidHandle);
return ERR_INVALID_HANDLE;
}
if (thread->GetOwnerProcess() != current_process) { // Require the handle be to a non-current thread in the current process.
LOG_ERROR(Kernel_SVC, R_UNLESS(thread->GetOwnerProcess() == current_process, Svc::ResultInvalidHandle);
"The current process does not own the current thread, thread_handle={:08X} " R_UNLESS(thread.get() != system.Kernel().CurrentScheduler()->GetCurrentThread(),
"thread_pid={}, " Svc::ResultBusy);
"current_process_pid={}",
handle, thread->GetOwnerProcess()->GetProcessID(),
current_process->GetProcessID());
return ERR_INVALID_HANDLE;
}
if (thread.get() == system.Kernel().CurrentScheduler()->GetCurrentThread()) { // Get the thread context.
LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread"); std::vector<u8> context;
return ERR_BUSY; R_TRY(thread->GetThreadContext3(context));
}
Core::ARM_Interface::ThreadContext64 ctx = thread->GetContext64(); // Copy the thread context to user space.
// Mask away mode bits, interrupt bits, IL bit, and other reserved bits. system.Memory().WriteBlock(out_context, context.data(), context.size());
ctx.pstate &= 0xFF0FFE20;
// 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{});
}
system.Memory().WriteBlock(thread_context, &ctx, sizeof(ctx));
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
static ResultCode GetThreadContext32(Core::System& system, u32 thread_context, Handle handle) { static ResultCode GetThreadContext32(Core::System& system, u32 out_context, Handle thread_handle) {
return GetThreadContext(system, thread_context, handle); return GetThreadContext(system, out_context, thread_handle);
} }
/// Gets the priority for the specified thread /// 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"); LOG_TRACE(Kernel_SVC, "called");
// Get the thread from its handle.
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable(); const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(handle); const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(handle);
if (!thread) { R_UNLESS(thread, Svc::ResultInvalidHandle);
*priority = 0;
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
return ERR_INVALID_HANDLE;
}
*priority = thread->GetPriority(); // Get the thread's priority.
*out_priority = thread->GetPriority();
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
static ResultCode GetThreadPriority32(Core::System& system, u32* priority, Handle handle) { static ResultCode GetThreadPriority32(Core::System& system, u32* out_priority, Handle handle) {
return GetThreadPriority(system, priority, handle); return GetThreadPriority(system, out_priority, handle);
} }
/// Sets the priority for the specified thread /// Sets the priority for the specified thread
static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 priority) { static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 priority) {
LOG_TRACE(Kernel_SVC, "called"); LOG_TRACE(Kernel_SVC, "called");
if (priority > THREADPRIO_LOWEST) { // Validate the priority.
LOG_ERROR( R_UNLESS(Svc::HighestThreadPriority <= priority && priority <= Svc::LowestThreadPriority,
Kernel_SVC, Svc::ResultInvalidPriority);
"An invalid priority was specified, expected {} but got {} for thread_handle={:08X}",
THREADPRIO_LOWEST, priority, handle);
return ERR_INVALID_THREAD_PRIORITY;
}
const auto* const current_process = system.Kernel().CurrentProcess(); // Get the thread from its handle.
const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
std::shared_ptr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(handle);
if (!thread) { R_UNLESS(thread, Svc::ResultInvalidHandle);
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
return ERR_INVALID_HANDLE;
}
// Set the thread priority.
thread->SetBasePriority(priority); thread->SetBasePriority(priority);
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
@ -1438,62 +1416,50 @@ static void ExitProcess(Core::System& system) {
current_process->PrepareForTermination(); current_process->PrepareForTermination();
// Kill the current thread // Kill the current thread
system.Kernel().CurrentScheduler()->GetCurrentThread()->Stop(); system.Kernel().CurrentScheduler()->GetCurrentThread()->Exit();
} }
static void ExitProcess32(Core::System& system) { static void ExitProcess32(Core::System& system) {
ExitProcess(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 /// Creates a new thread
static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr entry_point, u64 arg, 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, LOG_DEBUG(Kernel_SVC,
"called entrypoint=0x{:08X}, arg=0x{:08X}, stacktop=0x{:08X}, " "called entry_point=0x{:08X}, arg=0x{:08X}, stack_bottom=0x{:08X}, "
"threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}", "priority=0x{:08X}, core_id=0x{:08X}",
entry_point, arg, stack_top, priority, processor_id, *out_handle); 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);
}
if (processor_id < THREADPROCESSORID_0 || processor_id > THREADPROCESSORID_3) {
LOG_ERROR(Kernel_SVC, "Invalid thread processor ID: {}", processor_id);
return ERR_INVALID_PROCESSOR_ID;
}
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;
}
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;
}
if (((1ULL << priority) & current_process->GetPriorityMask()) == 0) {
LOG_ERROR(Kernel_SVC, "Invalid thread priority specified ({})", priority);
return ERR_INVALID_THREAD_PRIORITY;
}
// Adjust core id, if it's the default magic.
auto& kernel = system.Kernel(); auto& kernel = system.Kernel();
auto& process = *kernel.CurrentProcess();
if (core_id == Svc::IdealCoreUseProcessValue) {
core_id = process.GetIdealCoreId();
}
ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(ResourceType::Threads, 1)); // Validate arguments.
R_UNLESS(IsValidCoreId(core_id), Svc::ResultInvalidCoreId);
R_UNLESS(((1ULL << core_id) & process.GetCoreMask()) != 0, Svc::ResultInvalidCoreId);
ThreadType type = THREADTYPE_USER; R_UNLESS(Svc::HighestThreadPriority <= priority && priority <= Svc::LowestThreadPriority,
CASCADE_RESULT(std::shared_ptr<Thread> thread, Svc::ResultInvalidPriority);
Thread::Create(system, type, "", entry_point, priority, arg, processor_id, R_UNLESS(process.CheckThreadPriority(priority), Svc::ResultInvalidPriority);
stack_top, current_process));
const auto new_thread_handle = current_process->GetHandleTable().Create(thread); ASSERT(process.GetResourceLimit()->Reserve(ResourceType::Threads, 1));
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));
}
const auto new_thread_handle = process.GetHandleTable().Create(thread);
if (new_thread_handle.Failed()) { if (new_thread_handle.Failed()) {
LOG_ERROR(Kernel_SVC, "Failed to create handle with error=0x{:X}", LOG_ERROR(Kernel_SVC, "Failed to create handle with error=0x{:X}",
new_thread_handle.Code().raw); 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) { static ResultCode StartThread(Core::System& system, Handle thread_handle) {
LOG_DEBUG(Kernel_SVC, "called thread=0x{:08X}", 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 auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle); const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle);
if (!thread) { R_UNLESS(thread, Svc::ResultInvalidHandle);
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
thread_handle);
return ERR_INVALID_HANDLE;
}
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) { 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(); auto* const current_thread = system.Kernel().CurrentScheduler()->GetCurrentThread();
system.GlobalSchedulerContext().RemoveThread(SharedFrom(current_thread)); system.GlobalSchedulerContext().RemoveThread(SharedFrom(current_thread));
current_thread->Stop(); current_thread->Exit();
} }
static void ExitThread32(Core::System& system) { static void ExitThread32(Core::System& system) {
@ -1549,34 +1513,28 @@ static void ExitThread32(Core::System& system) {
/// Sleep the current thread /// Sleep the current thread
static void SleepThread(Core::System& system, s64 nanoseconds) { static void SleepThread(Core::System& system, s64 nanoseconds) {
auto& kernel = system.Kernel();
const auto yield_type = static_cast<Svc::YieldType>(nanoseconds);
LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds); LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds);
enum class SleepType : s64 { // When the input tick is positive, sleep.
YieldWithoutCoreMigration = 0, if (nanoseconds > 0) {
YieldWithCoreMigration = -1, // Convert the timeout from nanoseconds to ticks.
YieldAndWaitForLoadBalancing = -2, // NOTE: Nintendo does not use this conversion logic in WaitSynchronization...
};
auto& scheduler = *system.Kernel().CurrentScheduler(); // Sleep.
if (nanoseconds <= 0) { // NOTE: Nintendo does not check the result of this sleep.
switch (static_cast<SleepType>(nanoseconds)) { static_cast<void>(GetCurrentThread(kernel).Sleep(nanoseconds));
case SleepType::YieldWithoutCoreMigration: { } else if (yield_type == Svc::YieldType::WithoutCoreMigration) {
scheduler.YieldWithoutCoreMigration(); KScheduler::YieldWithoutCoreMigration(kernel);
break; } else if (yield_type == Svc::YieldType::WithCoreMigration) {
} KScheduler::YieldWithCoreMigration(kernel);
case SleepType::YieldWithCoreMigration: { } else if (yield_type == Svc::YieldType::ToAnyThread) {
scheduler.YieldWithCoreMigration(); KScheduler::YieldToAnyThread(kernel);
break;
}
case SleepType::YieldAndWaitForLoadBalancing: {
scheduler.YieldToAnyThread();
break;
}
default:
UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds);
}
} else { } 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); return CreateTransferMemory(system, handle, addr, size, permissions);
} }
static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, u32* core, static ResultCode GetThreadCoreMask(Core::System& system, Handle thread_handle, s32* out_core_id,
u64* mask) { u64* out_affinity_mask) {
LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle); 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 auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle); const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle);
if (!thread) { R_UNLESS(thread, Svc::ResultInvalidHandle);
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
thread_handle);
*core = 0;
*mask = 0;
return ERR_INVALID_HANDLE;
}
*core = thread->GetIdealCore(); // Get the core mask.
*mask = thread->GetAffinityMask().GetAffinityMask(); R_TRY(thread->GetCoreMask(out_core_id, out_affinity_mask));
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
static ResultCode GetThreadCoreMask32(Core::System& system, Handle thread_handle, u32* core, static ResultCode GetThreadCoreMask32(Core::System& system, Handle thread_handle, s32* out_core_id,
u32* mask_low, u32* mask_high) { u32* out_affinity_mask_low, u32* out_affinity_mask_high) {
u64 mask{}; u64 out_affinity_mask{};
const auto result = GetThreadCoreMask(system, thread_handle, core, &mask); const auto result = GetThreadCoreMask(system, thread_handle, out_core_id, &out_affinity_mask);
*mask_high = static_cast<u32>(mask >> 32); *out_affinity_mask_high = static_cast<u32>(out_affinity_mask >> 32);
*mask_low = static_cast<u32>(mask); *out_affinity_mask_low = static_cast<u32>(out_affinity_mask);
return result; 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) { u64 affinity_mask) {
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, core=0x{:X}, affinity_mask=0x{:016X}", LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, core_id=0x{:X}, affinity_mask=0x{:016X}",
thread_handle, core, affinity_mask); thread_handle, core_id, affinity_mask);
const auto* const current_process = system.Kernel().CurrentProcess(); const auto& current_process = *system.Kernel().CurrentProcess();
if (core == static_cast<u32>(THREADPROCESSORID_IDEAL)) { // Determine the core id/affinity mask.
const u8 ideal_cpu_core = current_process->GetIdealCore(); if (core_id == Svc::IdealCoreUseProcessValue) {
core_id = current_process.GetIdealCoreId();
ASSERT(ideal_cpu_core != static_cast<u8>(THREADPROCESSORID_IDEAL)); affinity_mask = (1ULL << core_id);
// Set the target CPU to the ideal core specified by the process.
core = ideal_cpu_core;
affinity_mask = 1ULL << core;
} else { } else {
const u64 core_mask = current_process->GetCoreMask(); // 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);
if ((core_mask | affinity_mask) != core_mask) { // Validate the core id.
LOG_ERROR( if (IsValidCoreId(core_id)) {
Kernel_SVC, R_UNLESS(((1ULL << core_id) & affinity_mask) != 0, Svc::ResultInvalidCombination);
"Invalid processor ID specified (core_mask=0x{:08X}, affinity_mask=0x{:016X})", } else {
core_mask, affinity_mask); R_UNLESS(core_id == Svc::IdealCoreNoUpdate || core_id == Svc::IdealCoreDontCare,
return ERR_INVALID_PROCESSOR_ID; Svc::ResultInvalidCoreId);
}
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;
} }
} }
const auto& handle_table = current_process->GetHandleTable(); // Get the thread from its handle.
const std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle); const auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
if (!thread) { const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(thread_handle);
LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}", R_UNLESS(thread, Svc::ResultInvalidHandle);
thread_handle);
return ERR_INVALID_HANDLE;
}
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) { u32 affinity_mask_low, u32 affinity_mask_high) {
const auto affinity_mask = u64{affinity_mask_low} | (u64{affinity_mask_high} << 32); 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) { 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(); kernel.EnterSVCProfile();
auto* thread = kernel.CurrentScheduler()->GetCurrentThread(); auto* thread = kernel.CurrentScheduler()->GetCurrentThread();
thread->SetContinuousOnSVC(true); thread->SetIsCallingSvc();
const FunctionDef* info = system.CurrentProcess()->Is64BitProcess() ? GetSVCInfo64(immediate) const FunctionDef* info = system.CurrentProcess()->Is64BitProcess() ? GetSVCInfo64(immediate)
: GetSVCInfo32(immediate); : GetSVCInfo32(immediate);
@ -2507,7 +2442,7 @@ void Call(Core::System& system, u32 immediate) {
kernel.ExitSVCProfile(); kernel.ExitSVCProfile();
if (!thread->IsContinuousOnSVC()) { if (!thread->IsCallingSvc()) {
auto* host_context = thread->GetHostContext().get(); auto* host_context = thread->GetHostContext().get();
host_context->Rewind(); host_context->Rewind();
} }

View file

@ -8,13 +8,18 @@
namespace Kernel::Svc { namespace Kernel::Svc {
constexpr ResultCode ResultNoSynchronizationObject{ErrorModule::Kernel, 57};
constexpr ResultCode ResultTerminationRequested{ErrorModule::Kernel, 59}; constexpr ResultCode ResultTerminationRequested{ErrorModule::Kernel, 59};
constexpr ResultCode ResultInvalidAddress{ErrorModule::Kernel, 102}; constexpr ResultCode ResultInvalidAddress{ErrorModule::Kernel, 102};
constexpr ResultCode ResultInvalidCurrentMemory{ErrorModule::Kernel, 106}; 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 ResultInvalidHandle{ErrorModule::Kernel, 114};
constexpr ResultCode ResultInvalidCombination{ErrorModule::Kernel, 116};
constexpr ResultCode ResultTimedOut{ErrorModule::Kernel, 117}; constexpr ResultCode ResultTimedOut{ErrorModule::Kernel, 117};
constexpr ResultCode ResultCancelled{ErrorModule::Kernel, 118}; constexpr ResultCode ResultCancelled{ErrorModule::Kernel, 118};
constexpr ResultCode ResultInvalidEnumValue{ErrorModule::Kernel, 120}; constexpr ResultCode ResultInvalidEnumValue{ErrorModule::Kernel, 120};
constexpr ResultCode ResultBusy{ErrorModule::Kernel, 122};
constexpr ResultCode ResultInvalidState{ErrorModule::Kernel, 125}; constexpr ResultCode ResultInvalidState{ErrorModule::Kernel, 125};
} // namespace Kernel::Svc } // namespace Kernel::Svc

View file

@ -77,4 +77,22 @@ enum class ArbitrationType : u32 {
WaitIfEqual = 2, 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 } // namespace Kernel::Svc

View file

@ -58,6 +58,14 @@ void SvcWrap64(Core::System& system) {
func(system, static_cast<u32>(Param(system, 0)), static_cast<u32>(Param(system, 1))).raw); 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)> template <ResultCode func(Core::System&, u32, u64, u64, u64)>
void SvcWrap64(Core::System& system) { void SvcWrap64(Core::System& system) {
FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1), FuncReturn(system, func(system, static_cast<u32>(Param(system, 0)), Param(system, 1),
@ -158,9 +166,18 @@ void SvcWrap64(Core::System& system) {
.raw); .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) { 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; u64 param_2 = 0;
const ResultCode retval = func(system, static_cast<u32>(Param(system, 2)), &param_1, &param_2); const ResultCode retval = func(system, static_cast<u32>(Param(system, 2)), &param_1, &param_2);
@ -473,12 +490,35 @@ void SvcWrap32(Core::System& system) {
FuncReturn(system, retval); 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), &param_1, &param_2, &param_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 // Used by SignalProcessWideKey32
template <void func(Core::System&, u32, s32)> template <void func(Core::System&, u32, s32)>
void SvcWrap32(Core::System& system) { void SvcWrap32(Core::System& system) {
func(system, static_cast<u32>(Param(system, 0)), static_cast<s32>(Param(system, 1))); 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 // Used by SetThreadPriority32
template <ResultCode func(Core::System&, Handle, u32)> template <ResultCode func(Core::System&, Handle, u32)>
void SvcWrap32(Core::System& system) { void SvcWrap32(Core::System& system) {
@ -487,7 +527,7 @@ void SvcWrap32(Core::System& system) {
FuncReturn(system, retval); FuncReturn(system, retval);
} }
// Used by SetThreadCoreMask32 // Used by SetMemoryAttribute32
template <ResultCode func(Core::System&, Handle, u32, u32, u32)> template <ResultCode func(Core::System&, Handle, u32, u32, u32)>
void SvcWrap32(Core::System& system) { void SvcWrap32(Core::System& system) {
const u32 retval = const u32 retval =
@ -497,6 +537,16 @@ void SvcWrap32(Core::System& system) {
FuncReturn(system, retval); 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 // Used by WaitProcessWideKeyAtomic32
template <ResultCode func(Core::System&, u32, u32, Handle, u32, u32)> template <ResultCode func(Core::System&, u32, u32, Handle, u32, u32)>
void SvcWrap32(Core::System& system) { void SvcWrap32(Core::System& system) {

View file

@ -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

View file

@ -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

View file

@ -8,8 +8,8 @@
#include "core/core_timing_util.h" #include "core/core_timing_util.h"
#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/k_scheduler.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/kernel.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/time_manager.h" #include "core/hle/kernel/time_manager.h"
namespace Kernel { namespace Kernel {
@ -18,50 +18,30 @@ TimeManager::TimeManager(Core::System& system_) : system{system_} {
time_manager_event_type = Core::Timing::CreateEvent( time_manager_event_type = Core::Timing::CreateEvent(
"Kernel::TimeManagerCallback", "Kernel::TimeManagerCallback",
[this](std::uintptr_t thread_handle, std::chrono::nanoseconds) { [this](std::uintptr_t thread_handle, std::chrono::nanoseconds) {
std::shared_ptr<Thread> thread; std::shared_ptr<KThread> thread;
{ {
std::lock_guard lock{mutex}; std::lock_guard lock{mutex};
const auto proper_handle = static_cast<Handle>(thread_handle); thread = SharedFrom<KThread>(reinterpret_cast<KThread*>(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->Wakeup();
}
}); });
} }
void TimeManager::ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 nanoseconds) { void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) {
std::lock_guard lock{mutex}; std::lock_guard lock{mutex};
event_handle = timetask->GetGlobalHandle();
if (nanoseconds > 0) { if (nanoseconds > 0) {
ASSERT(timetask); ASSERT(thread);
ASSERT(timetask->GetState() != ThreadState::Runnable); ASSERT(thread->GetState() != ThreadState::Runnable);
system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{nanoseconds}, system.CoreTiming().ScheduleEvent(std::chrono::nanoseconds{nanoseconds},
time_manager_event_type, event_handle); time_manager_event_type,
} else { reinterpret_cast<uintptr_t>(thread));
event_handle = InvalidHandle;
} }
cancelled_events[event_handle] = false;
} }
void TimeManager::UnscheduleTimeEvent(Handle event_handle) { void TimeManager::UnscheduleTimeEvent(KThread* thread) {
std::lock_guard lock{mutex}; std::lock_guard lock{mutex};
if (event_handle == InvalidHandle) { system.CoreTiming().UnscheduleEvent(time_manager_event_type,
return; 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) {
std::lock_guard lock{mutex};
const Handle event_handle = time_task->GetGlobalHandle();
UnscheduleTimeEvent(event_handle);
} }
} // namespace Kernel } // namespace Kernel

View file

@ -20,7 +20,7 @@ struct EventType;
namespace Kernel { namespace Kernel {
class Thread; class KThread;
/** /**
* The `TimeManager` takes care of scheduling time events on threads and executes their TimeUp * The `TimeManager` takes care of scheduling time events on threads and executes their TimeUp
@ -31,18 +31,14 @@ public:
explicit TimeManager(Core::System& system); explicit TimeManager(Core::System& system);
/// Schedule a time event on `timetask` thread that will expire in 'nanoseconds' /// 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(KThread* time_task, s64 nanoseconds);
void ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 nanoseconds);
/// Unschedule an existing time event /// Unschedule an existing time event
void UnscheduleTimeEvent(Handle event_handle); void UnscheduleTimeEvent(KThread* thread);
void CancelTimeEvent(Thread* time_task);
private: private:
Core::System& system; Core::System& system;
std::shared_ptr<Core::Timing::EventType> time_manager_event_type; std::shared_ptr<Core::Timing::EventType> time_manager_event_type;
std::unordered_map<Handle, bool> cancelled_events;
std::mutex mutex; std::mutex mutex;
}; };

View file

@ -72,6 +72,8 @@ public:
/// is closed. /// is closed.
ResultCode Reset(); ResultCode Reset();
void Finalize() override {}
private: private:
/// The base address for the memory managed by this instance. /// The base address for the memory managed by this instance.
VAddr base_address{}; VAddr base_address{};

View file

@ -4,10 +4,10 @@
#include <algorithm> #include <algorithm>
#include "common/assert.h" #include "common/assert.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h" #include "core/hle/kernel/object.h"
#include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/writable_event.h" #include "core/hle/kernel/writable_event.h"
namespace Kernel { namespace Kernel {
@ -38,8 +38,4 @@ void WritableEvent::Clear() {
readable->Clear(); readable->Clear();
} }
bool WritableEvent::IsSignaled() const {
return readable->IsSignaled();
}
} // namespace Kernel } // namespace Kernel

View file

@ -46,7 +46,8 @@ public:
void Signal(); void Signal();
void Clear(); void Clear();
bool IsSignaled() const;
void Finalize() override {}
private: private:
explicit WritableEvent(KernelCore& kernel); explicit WritableEvent(KernelCore& kernel);

View file

@ -8,9 +8,9 @@
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/core.h" #include "core/core.h"
#include "core/hle/ipc_helpers.h" #include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/writable_event.h" #include "core/hle/kernel/writable_event.h"
#include "core/hle/lock.h" #include "core/hle/lock.h"
#include "core/hle/service/nfp/nfp.h" #include "core/hle/service/nfp/nfp.h"

View file

@ -6,9 +6,9 @@
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/core.h" #include "core/core.h"
#include "core/hle/ipc_helpers.h" #include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/writable_event.h" #include "core/hle/kernel/writable_event.h"
#include "core/hle/service/nvdrv/interface.h" #include "core/hle/service/nvdrv/interface.h"
#include "core/hle/service/nvdrv/nvdata.h" #include "core/hle/service/nvdrv/nvdata.h"

View file

@ -11,10 +11,10 @@
#include "core/hle/ipc.h" #include "core/hle/ipc.h"
#include "core/hle/ipc_helpers.h" #include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_port.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/kernel.h"
#include "core/hle/kernel/process.h" #include "core/hle/kernel/process.h"
#include "core/hle/kernel/server_port.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/acc/acc.h"
#include "core/hle/service/am/am.h" #include "core/hle/service/am/am.h"
#include "core/hle/service/aoc/aoc_u.h" #include "core/hle/service/aoc/aoc_u.h"

View file

@ -13,7 +13,7 @@
#include "common/microprofile.h" #include "common/microprofile.h"
#include "common/thread.h" #include "common/thread.h"
#include "core/hle/ipc_helpers.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/bsd.h"
#include "core/hle/service/sockets/sockets_translate.h" #include "core/hle/service/sockets/sockets_translate.h"
#include "core/network/network.h" #include "core/network/network.h"

View file

@ -121,7 +121,7 @@ private:
}; };
ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal( 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) { Clock::SystemClockContext network_context, u8 type, Clock::ClockSnapshot& clock_snapshot) {
auto& time_manager{system.GetTimeManager()}; auto& time_manager{system.GetTimeManager()};

View file

@ -39,7 +39,7 @@ public:
private: private:
ResultCode GetClockSnapshotFromSystemClockContextInternal( ResultCode GetClockSnapshotFromSystemClockContextInternal(
Kernel::Thread* thread, Clock::SystemClockContext user_context, Kernel::KThread* thread, Clock::SystemClockContext user_context,
Clock::SystemClockContext network_context, u8 type, Clock::SystemClockContext network_context, u8 type,
Clock::ClockSnapshot& cloc_snapshot); Clock::ClockSnapshot& cloc_snapshot);

View file

@ -6,8 +6,8 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "common/uuid.h" #include "common/uuid.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/kernel/shared_memory.h" #include "core/hle/kernel/shared_memory.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/service/time/clock_types.h" #include "core/hle/service/time/clock_types.h"
namespace Service::Time { namespace Service::Time {

View file

@ -18,8 +18,8 @@
#include "common/swap.h" #include "common/swap.h"
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/hle/ipc_helpers.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/readable_event.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/writable_event.h" #include "core/hle/kernel/writable_event.h"
#include "core/hle/service/nvdrv/nvdata.h" #include "core/hle/service/nvdrv/nvdata.h"
#include "core/hle/service/nvdrv/nvdrv.h" #include "core/hle/service/nvdrv/nvdrv.h"

View file

@ -15,9 +15,9 @@
#include "core/file_sys/romfs_factory.h" #include "core/file_sys/romfs_factory.h"
#include "core/file_sys/vfs_offset.h" #include "core/file_sys/vfs_offset.h"
#include "core/hle/kernel/code_set.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/memory/page_table.h"
#include "core/hle/kernel/process.h" #include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/nro.h" #include "core/loader/nro.h"
#include "core/loader/nso.h" #include "core/loader/nso.h"
@ -219,8 +219,8 @@ AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process, Core::Sy
} }
is_loaded = true; is_loaded = true;
return {ResultStatus::Success, return {ResultStatus::Success, LoadParameters{Kernel::KThread::DefaultThreadPriority,
LoadParameters{Kernel::THREADPRIO_DEFAULT, Core::Memory::DEFAULT_STACK_SIZE}}; Core::Memory::DEFAULT_STACK_SIZE}};
} }
ResultStatus AppLoader_NRO::ReadIcon(std::vector<u8>& buffer) { ResultStatus AppLoader_NRO::ReadIcon(std::vector<u8>& buffer) {

View file

@ -15,9 +15,9 @@
#include "core/core.h" #include "core/core.h"
#include "core/file_sys/patch_manager.h" #include "core/file_sys/patch_manager.h"
#include "core/hle/kernel/code_set.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/memory/page_table.h"
#include "core/hle/kernel/process.h" #include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h"
#include "core/loader/nso.h" #include "core/loader/nso.h"
#include "core/memory.h" #include "core/memory.h"
#include "core/settings.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); LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address);
is_loaded = true; is_loaded = true;
return {ResultStatus::Success, return {ResultStatus::Success, LoadParameters{Kernel::KThread::DefaultThreadPriority,
LoadParameters{Kernel::THREADPRIO_DEFAULT, Core::Memory::DEFAULT_STACK_SIZE}}; Core::Memory::DEFAULT_STACK_SIZE}};
} }
ResultStatus AppLoader_NSO::ReadNSOModules(Modules& modules) { ResultStatus AppLoader_NSO::ReadNSOModules(Modules& modules) {

View file

@ -15,10 +15,11 @@
#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/k_scheduler.h" #include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/k_synchronization_object.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/process.h"
#include "core/hle/kernel/readable_event.h" #include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/svc_common.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" #include "core/memory.h"
namespace { 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>> WaitTreeItem::MakeThreadItemList() {
std::vector<std::unique_ptr<WaitTreeThread>> item_list; std::vector<std::unique_ptr<WaitTreeThread>> item_list;
std::size_t row = 0; 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) { 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.push_back(std::make_unique<WaitTreeThread>(*threads[i]));
item_list.back()->row = row; item_list.back()->row = row;
} }
@ -117,7 +118,7 @@ WaitTreeMutexInfo::WaitTreeMutexInfo(VAddr mutex_address, const Kernel::HandleTa
: mutex_address(mutex_address) { : mutex_address(mutex_address) {
mutex_value = Core::System::GetInstance().Memory().Read32(mutex_address); mutex_value = Core::System::GetInstance().Memory().Read32(mutex_address);
owner_handle = static_cast<Kernel::Handle>(mutex_value & Kernel::Svc::HandleWaitMask); 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; WaitTreeMutexInfo::~WaitTreeMutexInfo() = default;
@ -139,7 +140,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeMutexInfo::GetChildren() cons
return list; return list;
} }
WaitTreeCallstack::WaitTreeCallstack(const Kernel::Thread& thread) : thread(thread) {} WaitTreeCallstack::WaitTreeCallstack(const Kernel::KThread& thread) : thread(thread) {}
WaitTreeCallstack::~WaitTreeCallstack() = default; WaitTreeCallstack::~WaitTreeCallstack() = default;
QString WaitTreeCallstack::GetText() const { 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>> WaitTreeCallstack::GetChildren() const {
std::vector<std::unique_ptr<WaitTreeItem>> list; std::vector<std::unique_ptr<WaitTreeItem>> list;
if (thread.IsHLEThread()) { if (thread.GetThreadTypeForDebugging() != Kernel::ThreadType::User) {
return list; return list;
} }
@ -194,7 +195,7 @@ std::unique_ptr<WaitTreeSynchronizationObject> WaitTreeSynchronizationObject::ma
case Kernel::HandleType::ReadableEvent: case Kernel::HandleType::ReadableEvent:
return std::make_unique<WaitTreeEvent>(static_cast<const Kernel::ReadableEvent&>(object)); return std::make_unique<WaitTreeEvent>(static_cast<const Kernel::ReadableEvent&>(object));
case Kernel::HandleType::Thread: 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: default:
return std::make_unique<WaitTreeSynchronizationObject>(object); return std::make_unique<WaitTreeSynchronizationObject>(object);
} }
@ -231,21 +232,17 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeObjectList::GetChildren() con
return list; return list;
} }
WaitTreeThread::WaitTreeThread(const Kernel::Thread& thread) WaitTreeThread::WaitTreeThread(const Kernel::KThread& thread)
: WaitTreeSynchronizationObject(thread) {} : WaitTreeSynchronizationObject(thread) {}
WaitTreeThread::~WaitTreeThread() = default; WaitTreeThread::~WaitTreeThread() = default;
QString WaitTreeThread::GetText() const { QString WaitTreeThread::GetText() const {
const auto& thread = static_cast<const Kernel::Thread&>(object); const auto& thread = static_cast<const Kernel::KThread&>(object);
QString status; QString status;
switch (thread.GetState()) { switch (thread.GetState()) {
case Kernel::ThreadState::Runnable: case Kernel::ThreadState::Runnable:
if (!thread.IsPaused()) { if (!thread.IsSuspended()) {
if (thread.WasRunning()) { status = tr("runnable");
status = tr("running");
} else {
status = tr("ready");
}
} else { } else {
status = tr("paused"); status = tr("paused");
} }
@ -297,15 +294,11 @@ QString WaitTreeThread::GetText() const {
QColor WaitTreeThread::GetColor() const { QColor WaitTreeThread::GetColor() const {
const std::size_t color_index = IsDarkTheme() ? 1 : 0; 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()) { switch (thread.GetState()) {
case Kernel::ThreadState::Runnable: case Kernel::ThreadState::Runnable:
if (!thread.IsPaused()) { if (!thread.IsSuspended()) {
if (thread.WasRunning()) {
return QColor(WaitTreeColors[0][color_index]); return QColor(WaitTreeColors[0][color_index]);
} else {
return QColor(WaitTreeColors[1][color_index]);
}
} else { } else {
return QColor(WaitTreeColors[2][color_index]); 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>> WaitTreeThread::GetChildren() const {
std::vector<std::unique_ptr<WaitTreeItem>> list(WaitTreeSynchronizationObject::GetChildren()); 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; QString processor;
switch (thread.GetProcessorID()) { switch (thread.GetActiveCore()) {
case Kernel::ThreadProcessorId::THREADPROCESSORID_IDEAL: case Kernel::Svc::IdealCoreUseProcessValue:
processor = tr("ideal"); processor = tr("ideal");
break; 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: default:
processor = tr("Unknown processor %1").arg(thread.GetProcessorID()); processor = tr("core %1").arg(thread.GetActiveCore());
break; break;
} }
list.push_back(std::make_unique<WaitTreeText>(tr("processor = %1").arg(processor))); list.push_back(std::make_unique<WaitTreeText>(tr("processor = %1").arg(processor)));
list.push_back( list.push_back(std::make_unique<WaitTreeText>(
std::make_unique<WaitTreeText>(tr("ideal core = %1").arg(thread.GetIdealCore()))); tr("ideal core = %1").arg(thread.GetIdealCoreForDebugging())));
list.push_back(std::make_unique<WaitTreeText>( list.push_back(std::make_unique<WaitTreeText>(
tr("affinity mask = %1").arg(thread.GetAffinityMask().GetAffinityMask()))); tr("affinity mask = %1").arg(thread.GetAffinityMask().GetAffinityMask())));
list.push_back(std::make_unique<WaitTreeText>(tr("thread id = %1").arg(thread.GetThreadID()))); 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) {} : WaitTreeSynchronizationObject(object) {}
WaitTreeEvent::~WaitTreeEvent() = default; WaitTreeEvent::~WaitTreeEvent() = default;
WaitTreeThreadList::WaitTreeThreadList(const std::vector<Kernel::Thread*>& list) WaitTreeThreadList::WaitTreeThreadList(const std::vector<Kernel::KThread*>& list)
: thread_list(list) {} : thread_list(list) {}
WaitTreeThreadList::~WaitTreeThreadList() = default; WaitTreeThreadList::~WaitTreeThreadList() = default;

View file

@ -19,8 +19,8 @@ class EmuThread;
namespace Kernel { namespace Kernel {
class HandleTable; class HandleTable;
class KSynchronizationObject; class KSynchronizationObject;
class KThread;
class ReadableEvent; class ReadableEvent;
class Thread;
} // namespace Kernel } // namespace Kernel
class WaitTreeThread; class WaitTreeThread;
@ -83,20 +83,20 @@ private:
VAddr mutex_address; VAddr mutex_address;
u32 mutex_value; u32 mutex_value;
Kernel::Handle owner_handle; Kernel::Handle owner_handle;
std::shared_ptr<Kernel::Thread> owner; std::shared_ptr<Kernel::KThread> owner;
}; };
class WaitTreeCallstack : public WaitTreeExpandableItem { class WaitTreeCallstack : public WaitTreeExpandableItem {
Q_OBJECT Q_OBJECT
public: public:
explicit WaitTreeCallstack(const Kernel::Thread& thread); explicit WaitTreeCallstack(const Kernel::KThread& thread);
~WaitTreeCallstack() override; ~WaitTreeCallstack() override;
QString GetText() const override; QString GetText() const override;
std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
private: private:
const Kernel::Thread& thread; const Kernel::KThread& thread;
}; };
class WaitTreeSynchronizationObject : public WaitTreeExpandableItem { class WaitTreeSynchronizationObject : public WaitTreeExpandableItem {
@ -131,7 +131,7 @@ private:
class WaitTreeThread : public WaitTreeSynchronizationObject { class WaitTreeThread : public WaitTreeSynchronizationObject {
Q_OBJECT Q_OBJECT
public: public:
explicit WaitTreeThread(const Kernel::Thread& thread); explicit WaitTreeThread(const Kernel::KThread& thread);
~WaitTreeThread() override; ~WaitTreeThread() override;
QString GetText() const override; QString GetText() const override;
@ -149,14 +149,14 @@ public:
class WaitTreeThreadList : public WaitTreeExpandableItem { class WaitTreeThreadList : public WaitTreeExpandableItem {
Q_OBJECT Q_OBJECT
public: public:
explicit WaitTreeThreadList(const std::vector<Kernel::Thread*>& list); explicit WaitTreeThreadList(const std::vector<Kernel::KThread*>& list);
~WaitTreeThreadList() override; ~WaitTreeThreadList() override;
QString GetText() const override; QString GetText() const override;
std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override; std::vector<std::unique_ptr<WaitTreeItem>> GetChildren() const override;
private: private:
const std::vector<Kernel::Thread*>& thread_list; const std::vector<Kernel::KThread*>& thread_list;
}; };
class WaitTreeModel : public QAbstractItemModel { class WaitTreeModel : public QAbstractItemModel {

View file

@ -1039,8 +1039,6 @@ bool GMainWindow::LoadROM(const QString& filename, std::size_t program_index) {
std::make_unique<QtWebBrowser>(*this), // Web Browser std::make_unique<QtWebBrowser>(*this), // Web Browser
}); });
system.RegisterHostThread();
const Core::System::ResultStatus result{ const Core::System::ResultStatus result{
system.Load(*render_window, filename.toStdString(), program_index)}; system.Load(*render_window, filename.toStdString(), program_index)};