hle: kernel: Recode implementation of KThread to be more accurate.
This commit is contained in:
parent
1470338458
commit
cdd14b03e5
14 changed files with 1583 additions and 806 deletions
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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<KThread> thread = current_process->GetHandleTable().Get<KThread>(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<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);
|
|
||||||
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) {
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)), ¶m_1, ¶m_2);
|
const ResultCode retval = func(system, static_cast<u32>(Param(system, 2)), ¶m_1, ¶m_2);
|
||||||
|
|
||||||
|
@ -473,12 +490,35 @@ void SvcWrap32(Core::System& system) {
|
||||||
FuncReturn(system, retval);
|
FuncReturn(system, retval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used by GetThreadCoreMask32
|
||||||
|
template <ResultCode func(Core::System&, Handle, s32*, u32*, u32*)>
|
||||||
|
void SvcWrap32(Core::System& system) {
|
||||||
|
s32 param_1 = 0;
|
||||||
|
u32 param_2 = 0;
|
||||||
|
u32 param_3 = 0;
|
||||||
|
|
||||||
|
const u32 retval = func(system, Param32(system, 2), ¶m_1, ¶m_2, ¶m_3).raw;
|
||||||
|
system.CurrentArmInterface().SetReg(1, param_1);
|
||||||
|
system.CurrentArmInterface().SetReg(2, param_2);
|
||||||
|
system.CurrentArmInterface().SetReg(3, param_3);
|
||||||
|
FuncReturn(system, retval);
|
||||||
|
}
|
||||||
|
|
||||||
// Used by SignalProcessWideKey32
|
// 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) {
|
||||||
|
|
|
@ -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())));
|
||||||
|
|
Loading…
Reference in a new issue