hle: kernel: Recode implementation of KThread to be more accurate.

This commit is contained in:
bunnei 2021-01-20 13:42:27 -08:00
parent 1470338458
commit cdd14b03e5
14 changed files with 1583 additions and 806 deletions

View file

@ -279,8 +279,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->IsKernelThread()) {
idle_count = 0; idle_count = 0;
} }
} }

View file

@ -126,9 +126,6 @@ public:
return server_session; return server_session;
} }
using WakeupCallback = std::function<void(
std::shared_ptr<KThread> 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);

View file

@ -31,11 +31,15 @@ static void IncrementScheduledCount(Kernel::KThread* thread) {
} }
} }
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;
must_context_switch = true;
}
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));
@ -58,26 +62,25 @@ void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedul
u64 KScheduler::UpdateHighestPriorityThread(KThread* highest_thread) { u64 KScheduler::UpdateHighestPriorityThread(KThread* highest_thread) {
std::scoped_lock lock{guard}; std::scoped_lock lock{guard};
if (KThread* 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 = true;
return (1ULL << this->core_id); return (1ULL << core_id);
} else { } else {
return 0; return 0;
} }
@ -99,7 +102,20 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
KThread* 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);
} }
@ -182,6 +198,19 @@ u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
return cores_needing_scheduling; return cores_needing_scheduling;
} }
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) { void KScheduler::OnThreadStateChanged(KernelCore& kernel, KThread* thread, ThreadState old_state) {
ASSERT(kernel.GlobalSchedulerContext().IsLocked()); ASSERT(kernel.GlobalSchedulerContext().IsLocked());
@ -352,12 +381,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 +403,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.
KThread& 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.
@ -413,15 +442,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.
KThread& 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.
@ -503,15 +530,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.
KThread& 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.
@ -581,15 +606,14 @@ 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 = 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;
@ -613,7 +637,7 @@ void KScheduler::RescheduleCurrentCore() {
phys_core.ClearInterrupt(); phys_core.ClearInterrupt();
} }
guard.lock(); guard.lock();
if (this->state.needs_scheduling) { if (state.needs_scheduling) {
Schedule(); Schedule();
} else { } else {
guard.unlock(); guard.unlock();
@ -625,32 +649,34 @@ void KScheduler::OnThreadStart() {
} }
void KScheduler::Unload(KThread* 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()) {
system.ArmInterface(core_id).ExceptionalExit(); system.ArmInterface(core_id).ExceptionalExit();
thread->SetContinuousOnSVC(false); thread->ClearIsCallingSvc();
} }
if (!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();
} }
} }
void KScheduler::Reload(KThread* 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);
@ -676,7 +702,7 @@ void KScheduler::ScheduleImpl() {
KThread* previous_thread = current_thread; KThread* previous_thread = current_thread;
current_thread = state.highest_priority_thread; current_thread = state.highest_priority_thread;
this->state.needs_scheduling = false; state.needs_scheduling = false;
if (current_thread == previous_thread) { if (current_thread == previous_thread) {
guard.unlock(); guard.unlock();
@ -714,7 +740,7 @@ void KScheduler::SwitchToCurrent() {
{ {
std::scoped_lock lock{guard}; std::scoped_lock lock{guard};
current_thread = state.highest_priority_thread; current_thread = state.highest_priority_thread;
this->state.needs_scheduling = false; state.needs_scheduling = false;
} }
const auto is_switch_pending = [this] { const auto is_switch_pending = [this] {
std::scoped_lock lock{guard}; std::scoped_lock lock{guard};
@ -722,13 +748,10 @@ void KScheduler::SwitchToCurrent() {
}; };
do { do {
if (current_thread != nullptr) { if (current_thread != nullptr) {
current_thread->context_guard.lock();
if (current_thread->GetRawState() != ThreadState::Runnable) { if (current_thread->GetRawState() != ThreadState::Runnable) {
current_thread->context_guard.unlock();
break; break;
} }
if (static_cast<u32>(current_thread->GetProcessorID()) != core_id) { if (static_cast<u32>(current_thread->GetActiveCore()) != core_id) {
current_thread->context_guard.unlock();
break; break;
} }
} }
@ -749,7 +772,7 @@ void KScheduler::UpdateLastContextSwitchTime(KThread* thread, Process* process)
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) {
@ -763,15 +786,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();
auto thread_res = KThread::Create(system, ThreadType::Kernel, name, 0, auto thread_res = KThread::Create(system, ThreadType::Main, name, 0,
KThread::IdleThreadPriority, 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

@ -33,15 +33,14 @@ 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.
@ -53,6 +52,11 @@ public:
/// Gets the current running thread /// Gets the current running thread
[[nodiscard]] KThread* 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;
@ -79,7 +83,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 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,7 +101,9 @@ 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, KThread* thread, ThreadState old_state); static void OnThreadStateChanged(KernelCore& kernel, KThread* thread, ThreadState old_state);
@ -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:
@ -168,6 +173,7 @@ private:
static void OnSwitch(void* this_scheduler); static void OnSwitch(void* this_scheduler);
void SwitchToCurrent(); void SwitchToCurrent();
KThread* prev_thread{};
KThread* current_thread{}; KThread* current_thread{};
KThread* idle_thread{}; KThread* idle_thread{};
@ -186,7 +192,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{};
}; };

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,10 @@
// Copyright 2014 Citra Emulator Project / PPSSPP Project // Copyright 2021 yuzu Emulator Project
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#pragma once #pragma once
#include <array> #include <array>
#include <functional>
#include <span> #include <span>
#include <string> #include <string>
#include <utility> #include <utility>
@ -18,9 +17,11 @@
#include "common/spin_lock.h" #include "common/spin_lock.h"
#include "core/arm/arm_interface.h" #include "core/arm/arm_interface.h"
#include "core/hle/kernel/k_affinity_mask.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/k_synchronization_object.h"
#include "core/hle/kernel/object.h" #include "core/hle/kernel/object.h"
#include "core/hle/kernel/svc_common.h" #include "core/hle/kernel/svc_common.h"
#include "core/hle/kernel/svc_types.h"
#include "core/hle/result.h" #include "core/hle/result.h"
namespace Common { namespace Common {
@ -38,6 +39,9 @@ class GlobalSchedulerContext;
class KernelCore; class KernelCore;
class Process; class Process;
class KScheduler; class KScheduler;
class KThreadQueue;
using KThreadFunction = VAddr;
enum class ThreadType : u32 { enum class ThreadType : u32 {
Main = 0, Main = 0,
@ -47,6 +51,16 @@ enum class ThreadType : u32 {
}; };
DECLARE_ENUM_FLAG_OPERATORS(ThreadType); DECLARE_ENUM_FLAG_OPERATORS(ThreadType);
enum class SuspendType : u32 {
Process = 0,
Thread = 1,
Debug = 2,
Backtrace = 3,
Init = 4,
Count,
};
enum class ThreadState : u16 { enum class ThreadState : u16 {
Initialized = 0, Initialized = 0,
Waiting = 1, Waiting = 1,
@ -66,21 +80,9 @@ enum class ThreadState : u16 {
}; };
DECLARE_ENUM_FLAG_OPERATORS(ThreadState); DECLARE_ENUM_FLAG_OPERATORS(ThreadState);
enum class ThreadWakeupReason { enum class DpcFlag : u32 {
Signal, // The thread was woken up by WakeupAllWaitingThreads due to an object signal. Terminating = (1 << 0),
Timeout // The thread was woken up due to a wait timeout. Terminated = (1 << 1),
};
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 { enum class ThreadWaitReasonForDebugging : u32 {
@ -93,21 +95,25 @@ enum class ThreadWaitReasonForDebugging : u32 {
Suspended, ///< Thread is waiting due to process suspension 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<> { class KThread final : public KSynchronizationObject, public boost::intrusive::list_base_hook<> {
friend class KScheduler; friend class KScheduler;
friend class Process; friend class Process;
public: public:
static constexpr s32 DefaultThreadPriority = 44; static constexpr s32 DefaultThreadPriority = 44;
static constexpr s32 IdleThreadPriority = 64; static constexpr s32 IdleThreadPriority = Svc::LowestThreadPriority + 1;
explicit KThread(KernelCore& kernel); explicit KThread(KernelCore& kernel);
~KThread() override; ~KThread() override;
using MutexWaitingThreads = std::vector<std::shared_ptr<KThread>>; public:
using ThreadContext32 = Core::ARM_Interface::ThreadContext32; using ThreadContext32 = Core::ARM_Interface::ThreadContext32;
using ThreadContext64 = Core::ARM_Interface::ThreadContext64; using ThreadContext64 = Core::ARM_Interface::ThreadContext64;
using WaiterList = boost::intrusive::list<KThread>;
/** /**
* Creates and returns a new thread. The new thread is immediately scheduled * Creates and returns a new thread. The new thread is immediately scheduled
@ -121,10 +127,9 @@ public:
* @param owner_process The parent process for the thread, if null, it's a kernel thread * @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 * @return A shared pointer to the newly created thread
*/ */
static ResultVal<std::shared_ptr<KThread>> Create(Core::System& system, ThreadType type_flags, [[nodiscard]] static ResultVal<std::shared_ptr<KThread>> Create(
std::string name, VAddr entry_point, Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point,
u32 priority, u64 arg, s32 processor_id, u32 priority, u64 arg, s32 processor_id, VAddr stack_top, Process* owner_process);
VAddr stack_top, Process* owner_process);
/** /**
* Creates and returns a new thread. The new thread is immediately scheduled * Creates and returns a new thread. The new thread is immediately scheduled
@ -140,12 +145,12 @@ public:
* @param thread_start_parameter The parameter which will passed to host context on init * @param thread_start_parameter The parameter which will passed to host context on init
* @return A shared pointer to the newly created thread * @return A shared pointer to the newly created thread
*/ */
static ResultVal<std::shared_ptr<KThread>> Create( [[nodiscard]] static ResultVal<std::shared_ptr<KThread>> Create(
Core::System& system, ThreadType type_flags, std::string name, VAddr entry_point, 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, 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::function<void(void*)>&& thread_start_func, void* thread_start_parameter);
std::string GetName() const override { [[nodiscard]] std::string GetName() const override {
return name; return name;
} }
@ -153,12 +158,12 @@ public:
name = std::move(new_name); name = std::move(new_name);
} }
std::string GetTypeName() const override { [[nodiscard]] std::string GetTypeName() const override {
return "Thread"; return "Thread";
} }
static constexpr HandleType HANDLE_TYPE = HandleType::Thread; static constexpr HandleType HANDLE_TYPE = HandleType::Thread;
HandleType GetHandleType() const override { [[nodiscard]] HandleType GetHandleType() const override {
return HANDLE_TYPE; return HANDLE_TYPE;
} }
@ -167,15 +172,15 @@ public:
* @return The current thread's priority * @return The current thread's priority
*/ */
[[nodiscard]] s32 GetPriority() const { [[nodiscard]] s32 GetPriority() const {
return current_priority; return priority;
} }
/** /**
* Sets the thread's current priority. * Sets the thread's current priority.
* @param priority The new priority. * @param priority The new priority.
*/ */
void SetPriority(s32 priority) { void SetPriority(s32 value) {
current_priority = priority; priority = value;
} }
/** /**
@ -186,15 +191,6 @@ public:
return base_priority; 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 * Gets the thread's thread ID
* @return The thread's ID * @return The thread's ID
@ -203,46 +199,67 @@ public:
return thread_id; return thread_id;
} }
/// Resumes a thread from waiting void ContinueIfHasKernelWaiters() {
if (GetNumKernelWaiters() > 0) {
Continue();
}
}
void Wakeup(); void Wakeup();
ResultCode Start(); void SetBasePriority(s32 value);
virtual bool IsSignaled() const override; [[nodiscard]] ResultCode Run();
/// Cancels a waiting operation that this thread may or may not be within. void Exit();
///
/// 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); [[nodiscard]] u32 GetSuspendFlags() const {
return suspend_allowed_flags & suspend_request_flags;
void SetSyncedObject(KSynchronizationObject* object, ResultCode result) {
SetSynchronizationResults(object, result);
} }
ResultCode GetWaitResult(KSynchronizationObject** out) const { [[nodiscard]] bool IsSuspended() const {
*out = signaling_object; return GetSuspendFlags() != 0;
return signaling_result;
} }
ResultCode GetSignalingResult() const { [[nodiscard]] bool IsSuspendRequested(SuspendType type) const {
return signaling_result; return (suspend_request_flags &
(1u << (static_cast<u32>(ThreadState::SuspendShift) + static_cast<u32>(type)))) !=
0;
} }
/** [[nodiscard]] bool IsSuspendRequested() const {
* Stops a thread, invalidating it from further use return suspend_request_flags != 0;
*/ }
void Stop();
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 the Thread Local Storage address of the current thread
* @returns VAddr of the thread's TLS * @returns VAddr of the thread's TLS
*/ */
VAddr GetTLSAddress() const { [[nodiscard]] VAddr GetTLSAddress() const {
return tls_address; return tls_address;
} }
@ -250,62 +267,45 @@ public:
* Returns the value of the TPIDR_EL0 Read/Write system register for this thread. * Returns the value of the TPIDR_EL0 Read/Write system register for this thread.
* @returns The value of the TPIDR_EL0 register. * @returns The value of the TPIDR_EL0 register.
*/ */
u64 GetTPIDR_EL0() const { [[nodiscard]] u64 GetTPIDR_EL0() const {
return tpidr_el0; return thread_context_64.tpidr;
} }
/// Sets the value of the TPIDR_EL0 Read/Write system register for this thread. /// Sets the value of the TPIDR_EL0 Read/Write system register for this thread.
void SetTPIDR_EL0(u64 value) { void SetTPIDR_EL0(u64 value) {
tpidr_el0 = value; thread_context_64.tpidr = value;
thread_context_32.tpidr = static_cast<u32>(value);
} }
/* [[nodiscard]] ThreadContext32& GetContext32() {
* Returns the address of the current thread's command buffer, located in the TLS. return thread_context_32;
* @returns VAddr of the thread's command buffer.
*/
VAddr GetCommandBufferAddress() const;
ThreadContext32& GetContext32() {
return context_32;
} }
const ThreadContext32& GetContext32() const { [[nodiscard]] const ThreadContext32& GetContext32() const {
return context_32; return thread_context_32;
} }
ThreadContext64& GetContext64() { [[nodiscard]] ThreadContext64& GetContext64() {
return context_64; return thread_context_64;
} }
const ThreadContext64& GetContext64() const { [[nodiscard]] const ThreadContext64& GetContext64() const {
return context_64; return thread_context_64;
} }
bool IsKernelThread() const { [[nodiscard]] std::shared_ptr<Common::Fiber>& GetHostContext();
return type == ThreadType::Kernel;
}
bool WasRunning() const { [[nodiscard]] ThreadState GetState() 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; return thread_state & ThreadState::Mask;
} }
ThreadState GetRawState() const { [[nodiscard]] ThreadState GetRawState() const {
return thread_state; return thread_state;
} }
void SetState(ThreadState state); void SetState(ThreadState state);
s64 GetLastScheduledTick() const { [[nodiscard]] s64 GetLastScheduledTick() const {
return last_scheduled_tick; return last_scheduled_tick;
} }
@ -313,43 +313,44 @@ public:
last_scheduled_tick = tick; last_scheduled_tick = tick;
} }
u64 GetTotalCPUTimeTicks() const { void AddCpuTime([[maybe_unused]] s32 core_id_, s64 amount) {
return total_cpu_time_ticks; cpu_time += amount;
// TODO(bunnei): Debug kernels track per-core tick counts. Should we?
} }
void UpdateCPUTimeTicks(u64 ticks) { [[nodiscard]] s64 GetCpuTime() const {
total_cpu_time_ticks += ticks; return cpu_time;
} }
s32 GetProcessorID() const { [[nodiscard]] s32 GetActiveCore() const {
return processor_id; return core_id;
} }
s32 GetActiveCore() const { void SetActiveCore(s32 core) {
return GetProcessorID(); core_id = core;
} }
void SetProcessorID(s32 new_core) { [[nodiscard]] s32 GetCurrentCore() const {
processor_id = new_core; return current_core_id;
} }
void SetActiveCore(s32 new_core) { void SetCurrentCore(s32 core) {
processor_id = new_core; current_core_id = core;
} }
Process* GetOwnerProcess() { [[nodiscard]] Process* GetOwnerProcess() {
return owner_process; return parent;
} }
const Process* GetOwnerProcess() const { [[nodiscard]] const Process* GetOwnerProcess() const {
return owner_process; return parent;
} }
const MutexWaitingThreads& GetMutexWaitingThreads() const { [[nodiscard]] bool IsUserThread() const {
return wait_mutex_threads; return parent != nullptr;
} }
KThread* GetLockOwner() const { [[nodiscard]] KThread* GetLockOwner() const {
return lock_owner; return lock_owner;
} }
@ -357,20 +358,21 @@ public:
lock_owner = owner; lock_owner = owner;
} }
u32 GetIdealCore() const { [[nodiscard]] const KAffinityMask& GetAffinityMask() const {
return ideal_core; return physical_affinity_mask;
} }
const KAffinityMask& GetAffinityMask() const { [[nodiscard]] ResultCode GetCoreMask(s32* out_ideal_core, u64* out_affinity_mask);
return affinity_mask;
}
ResultCode SetActivity(ThreadActivity value); [[nodiscard]] ResultCode GetPhysicalCoreMask(s32* out_ideal_core, u64* out_affinity_mask);
/// Sleeps this thread for the given amount of nanoseconds. [[nodiscard]] ResultCode SetCoreMask(s32 core_id, u64 v_affinity_mask);
ResultCode Sleep(s64 nanoseconds);
s64 GetYieldScheduleCount() const { [[nodiscard]] ResultCode SetActivity(Svc::ThreadActivity activity);
[[nodiscard]] ResultCode Sleep(s64 timeout);
[[nodiscard]] s64 GetYieldScheduleCount() const {
return schedule_count; return schedule_count;
} }
@ -378,56 +380,49 @@ public:
schedule_count = count; schedule_count = count;
} }
bool IsRunning() const { void WaitCancel();
return is_running;
[[nodiscard]] bool IsWaitCancelled() const {
return wait_cancelled;
} }
void SetIsRunning(bool value) { [[nodiscard]] void ClearWaitCancelled() {
is_running = value; wait_cancelled = false;
} }
bool IsWaitCancelled() const { [[nodiscard]] bool IsCancellable() const {
return is_sync_cancelled; return cancellable;
}
void ClearWaitCancelled() {
is_sync_cancelled = false;
}
Handle GetGlobalHandle() const {
return global_handle;
}
bool IsCancellable() const {
return is_cancellable;
} }
void SetCancellable() { void SetCancellable() {
is_cancellable = true; cancellable = true;
} }
void ClearCancellable() { void ClearCancellable() {
is_cancellable = false; cancellable = false;
} }
bool IsTerminationRequested() const { [[nodiscard]] bool IsTerminationRequested() const {
return will_be_terminated || GetRawState() == ThreadState::Terminated; return termination_requested || GetRawState() == ThreadState::Terminated;
} }
bool IsPaused() const { struct StackParameters {
return pausing_state != 0; 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;
} }
bool IsContinuousOnSVC() const { [[nodiscard]] const StackParameters& GetStackParameters() const {
return is_continuous_on_svc; return stack_parameters;
}
void SetContinuousOnSVC(bool is_continuous) {
is_continuous_on_svc = is_continuous;
}
bool HasExited() const {
return has_exited;
} }
class QueueEntry { class QueueEntry {
@ -457,26 +452,78 @@ public:
KThread* next{}; KThread* next{};
}; };
QueueEntry& GetPriorityQueueEntry(s32 core) { [[nodiscard]] QueueEntry& GetPriorityQueueEntry(s32 core) {
return per_core_priority_queue_entry[core]; return per_core_priority_queue_entry[core];
} }
const QueueEntry& GetPriorityQueueEntry(s32 core) const { [[nodiscard]] const QueueEntry& GetPriorityQueueEntry(s32 core) const {
return per_core_priority_queue_entry[core]; return per_core_priority_queue_entry[core];
} }
s32 GetDisableDispatchCount() const { void SetSleepingQueue(KThreadQueue* q) {
return disable_count; sleeping_queue = q;
}
[[nodiscard]] s32 GetDisableDispatchCount() const {
return this->GetStackParameters().disable_count;
} }
void DisableDispatch() { void DisableDispatch() {
ASSERT(GetDisableDispatchCount() >= 0); ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() >= 0);
disable_count++; this->GetStackParameters().disable_count++;
} }
void EnableDispatch() { void EnableDispatch() {
ASSERT(GetDisableDispatchCount() > 0); ASSERT(GetCurrentThread(kernel).GetDisableDispatchCount() > 0);
disable_count--; 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) { void SetWaitReasonForDebugging(ThreadWaitReasonForDebugging reason) {
@ -507,10 +554,16 @@ public:
return mutex_wait_address_for_debugging; return mutex_wait_address_for_debugging;
} }
[[nodiscard]] s32 GetIdealCoreForDebugging() const {
return virtual_ideal_core_id;
}
void AddWaiter(KThread* thread); void AddWaiter(KThread* thread);
void RemoveWaiter(KThread* thread); void RemoveWaiter(KThread* thread);
[[nodiscard]] ResultCode GetThreadContext3(std::vector<u8>& out);
[[nodiscard]] KThread* RemoveWaiterByKey(s32* out_num_waiters, VAddr key); [[nodiscard]] KThread* RemoveWaiterByKey(s32* out_num_waiters, VAddr key);
[[nodiscard]] VAddr GetAddressKey() const { [[nodiscard]] VAddr GetAddressKey() const {
@ -530,6 +583,22 @@ public:
address_key_value = val; 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: private:
static constexpr size_t PriorityInheritanceCountMax = 10; static constexpr size_t PriorityInheritanceCountMax = 10;
union SyncObjectBuffer { union SyncObjectBuffer {
@ -560,8 +629,8 @@ private:
std::same_as<T, KThread> || std::same_as<T, KThread> ||
std::same_as<T, LightCompareType>) static constexpr int Compare(const T& lhs, std::same_as<T, LightCompareType>) static constexpr int Compare(const T& lhs,
const KThread& rhs) { const KThread& rhs) {
const uintptr_t l_key = lhs.GetConditionVariableKey(); const u64 l_key = lhs.GetConditionVariableKey();
const uintptr_t r_key = rhs.GetConditionVariableKey(); const u64 r_key = rhs.GetConditionVariableKey();
if (l_key < r_key) { if (l_key < r_key) {
// Sort first by key // Sort first by key
@ -575,26 +644,88 @@ private:
} }
}; };
Common::IntrusiveRedBlackTreeNode condvar_arbiter_tree_node{}; 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 = using ConditionVariableThreadTreeTraits =
Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert< Common::IntrusiveRedBlackTreeMemberTraitsDeferredAssert<
&KThread::condvar_arbiter_tree_node>; &KThread::condvar_arbiter_tree_node>;
using ConditionVariableThreadTree = using ConditionVariableThreadTree =
ConditionVariableThreadTreeTraits::TreeType<ConditionVariableComparator>; 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{};
// 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{};
std::string name;
public: public:
using ConditionVariableThreadTreeType = ConditionVariableThreadTree; using ConditionVariableThreadTreeType = ConditionVariableThreadTree;
[[nodiscard]] uintptr_t GetConditionVariableKey() const { void SetConditionVariable(ConditionVariableThreadTree* tree, VAddr address, u64 cv_key,
return condvar_key;
}
[[nodiscard]] uintptr_t GetAddressArbiterKey() const {
return condvar_key;
}
void SetConditionVariable(ConditionVariableThreadTree* tree, VAddr address, uintptr_t cv_key,
u32 value) { u32 value) {
condvar_tree = tree; condvar_tree = tree;
condvar_key = cv_key; condvar_key = cv_key;
@ -610,7 +741,7 @@ public:
return condvar_tree != nullptr; return condvar_tree != nullptr;
} }
void SetAddressArbiter(ConditionVariableThreadTree* tree, uintptr_t address) { void SetAddressArbiter(ConditionVariableThreadTree* tree, u64 address) {
condvar_tree = tree; condvar_tree = tree;
condvar_key = address; condvar_key = address;
} }
@ -626,111 +757,6 @@ public:
[[nodiscard]] ConditionVariableThreadTree* GetConditionVariableTree() const { [[nodiscard]] ConditionVariableThreadTree* GetConditionVariableTree() const {
return condvar_tree; return condvar_tree;
} }
[[nodiscard]] bool HasWaiters() const {
return !waiter_list.empty();
}
private:
void AddSchedulingFlag(ThreadSchedFlags flag);
void RemoveSchedulingFlag(ThreadSchedFlags flag);
void AddWaiterImpl(KThread* thread);
void RemoveWaiterImpl(KThread* thread);
static void RestorePriority(KernelCore& kernel, KThread* 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.
KThread* 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 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<KThread>;
WaiterList waiter_list{};
WaiterList pinned_waiter_list{};
std::string name;
}; };
} // namespace Kernel } // namespace Kernel

View file

@ -117,14 +117,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 (s32 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 (s32 i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
cores[i].Scheduler().Initialize(); cores[i].Scheduler().Initialize();
} }
} }
@ -169,9 +169,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();
auto thread_res = KThread::Create(system, ThreadType::Kernel, std::move(name), 0, 0, 0, auto thread_res = KThread::Create(system, ThreadType::HighPriority, std::move(name), 0,
static_cast<u32>(i), 0, nullptr, std::move(init_func), 0, 0, static_cast<u32>(i), 0, nullptr,
init_func_parameter); std::move(init_func), init_func_parameter);
suspend_threads[i] = std::move(thread_res).Unwrap(); suspend_threads[i] = std::move(thread_res).Unwrap();
} }

View file

@ -136,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 +
@ -161,6 +178,61 @@ u64 Process::GetTotalPhysicalMemoryUsedWithoutSystemResource() const {
return GetTotalPhysicalMemoryUsed() - GetSystemResourceUsage(); return GetTotalPhysicalMemoryUsed() - GetSystemResourceUsage();
} }
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) { void Process::RegisterThread(const KThread* thread) {
thread_list.push_back(thread); thread_list.push_back(thread);
} }
@ -278,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();
} }
}; };

View file

@ -217,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;
@ -237,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);
@ -310,6 +345,9 @@ public:
void Finalize() override {} void Finalize() override {}
void PinCurrentThread();
void UnpinCurrentThread();
/////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////
// Thread-local storage management // Thread-local storage management
@ -320,6 +358,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.
@ -408,6 +460,17 @@ 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{};
/// System context /// System context
Core::System& system; Core::System& system;

View file

@ -154,7 +154,7 @@ 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);
} }
} }

View file

@ -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<KThread> thread = handle_table.Get<KThread>(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;
} }
@ -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<KThread> thread = handle_table.Get<KThread>(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();
} }
} }
@ -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,127 +1023,109 @@ static ResultCode UnmapPhysicalMemory32(Core::System& system, u32 addr, u32 size
return UnmapPhysicalMemory(system, addr, size); return UnmapPhysicalMemory(system, addr, size);
} }
constexpr bool IsValidThreadActivity(Svc::ThreadActivity thread_activity) {
switch (thread_activity) {
case Svc::ThreadActivity::Runnable:
case Svc::ThreadActivity::Paused:
return true;
default:
return false;
}
}
/// Sets the thread activity /// Sets the thread activity
static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 activity) { static ResultCode SetThreadActivity(Core::System& system, Handle thread_handle,
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity); Svc::ThreadActivity thread_activity) {
if (activity > static_cast<u32>(ThreadActivity::Paused)) { LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", thread_handle,
return ERR_INVALID_ENUM_VALUE; 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;
} }
const auto* current_process = system.Kernel().CurrentProcess(); static ResultCode SetThreadActivity32(Core::System& system, Handle thread_handle,
const std::shared_ptr<KThread> thread = current_process->GetHandleTable().Get<KThread>(handle); Svc::ThreadActivity thread_activity) {
if (!thread) { return SetThreadActivity(system, thread_handle, thread_activity);
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) {
return SetThreadActivity(system, handle, 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<KThread> thread = current_process->GetHandleTable().Get<KThread>(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<KThread> thread = handle_table.Get<KThread>(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 > Svc::LowestThreadPriority) { // Validate the priority.
LOG_ERROR(Kernel_SVC, "An invalid priority was specified {} for thread_handle={:08X}", R_UNLESS(Svc::HighestThreadPriority <= priority && priority <= Svc::LowestThreadPriority,
priority, handle); Svc::ResultInvalidPriority);
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<KThread> thread = current_process->GetHandleTable().Get<KThread>(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;
} }
@ -1436,7 +1416,7 @@ 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) {
@ -1500,17 +1480,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<KThread> thread = handle_table.Get<KThread>(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) {
@ -1523,7 +1501,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) {
@ -1532,34 +1510,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);
} }
} }
@ -1822,95 +1794,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<KThread> thread = handle_table.Get<KThread>(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>(Svc::IdealCoreUseProcessValue)) { // Determine the core id/affinity mask.
const u8 ideal_cpu_core = current_process->GetIdealCoreId(); if (core_id == Svc::IdealCoreUseProcessValue) {
core_id = current_process.GetIdealCoreId();
ASSERT(ideal_cpu_core != static_cast<u8>(Svc::IdealCoreUseProcessValue)); 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>(Svc::IdealCoreDontCare) &&
core != static_cast<u32>(Svc::IdealCoreNoUpdate)) {
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 auto& handle_table = system.Kernel().CurrentProcess()->GetHandleTable();
const std::shared_ptr<KThread> thread = handle_table.Get<KThread>(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); // Set the core mask.
return ERR_INVALID_HANDLE; R_TRY(thread->SetCoreMask(core_id, affinity_mask));
return RESULT_SUCCESS;
} }
return thread->SetCoreAndAffinityMask(core, affinity_mask); static ResultCode SetThreadCoreMask32(Core::System& system, Handle thread_handle, s32 core_id,
}
static ResultCode SetThreadCoreMask32(Core::System& system, Handle thread_handle, u32 core,
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) {
@ -2474,7 +2423,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);
@ -2490,7 +2439,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

@ -77,6 +77,12 @@ enum class ArbitrationType : u32 {
WaitIfEqual = 2, WaitIfEqual = 2,
}; };
enum class YieldType : s64 {
WithoutCoreMigration = 0,
WithCoreMigration = -1,
ToAnyThread = -2,
};
enum class ThreadActivity : u32 { enum class ThreadActivity : u32 {
Runnable = 0, Runnable = 0,
Paused = 1, Paused = 1,

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

@ -235,12 +235,8 @@ QString WaitTreeThread::GetText() const {
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");
} }
@ -295,12 +291,8 @@ QColor WaitTreeThread::GetColor() const {
const auto& thread = static_cast<const Kernel::KThread&>(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]);
} }
@ -334,18 +326,18 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const {
const auto& thread = static_cast<const Kernel::KThread&>(object); const auto& thread = static_cast<const Kernel::KThread&>(object);
QString processor; QString processor;
switch (thread.GetProcessorID()) { switch (thread.GetActiveCore()) {
case Kernel::Svc::IdealCoreUseProcessValue: case Kernel::Svc::IdealCoreUseProcessValue:
processor = tr("ideal"); processor = tr("ideal");
break; break;
default: default:
processor = tr("core %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())));