Addapt thread class to the new Scheduler
This commit is contained in:
parent
b164d8ee53
commit
a1ac0c6cb4
2 changed files with 237 additions and 60 deletions
|
@ -45,15 +45,7 @@ void Thread::Stop() {
|
||||||
callback_handle);
|
callback_handle);
|
||||||
kernel.ThreadWakeupCallbackHandleTable().Close(callback_handle);
|
kernel.ThreadWakeupCallbackHandleTable().Close(callback_handle);
|
||||||
callback_handle = 0;
|
callback_handle = 0;
|
||||||
|
SetStatus(ThreadStatus::Dead);
|
||||||
// Clean up thread from ready queue
|
|
||||||
// This is only needed when the thread is terminated forcefully (SVC TerminateProcess)
|
|
||||||
if (status == ThreadStatus::Ready || status == ThreadStatus::Paused) {
|
|
||||||
scheduler->UnscheduleThread(this, current_priority);
|
|
||||||
}
|
|
||||||
|
|
||||||
status = ThreadStatus::Dead;
|
|
||||||
|
|
||||||
WakeupAllWaitingThreads();
|
WakeupAllWaitingThreads();
|
||||||
|
|
||||||
// Clean up any dangling references in objects that this thread was waiting for
|
// Clean up any dangling references in objects that this thread was waiting for
|
||||||
|
@ -132,13 +124,11 @@ void Thread::ResumeFromWait() {
|
||||||
wakeup_callback = nullptr;
|
wakeup_callback = nullptr;
|
||||||
|
|
||||||
if (activity == ThreadActivity::Paused) {
|
if (activity == ThreadActivity::Paused) {
|
||||||
status = ThreadStatus::Paused;
|
SetStatus(ThreadStatus::Paused);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = ThreadStatus::Ready;
|
SetStatus(ThreadStatus::Ready);
|
||||||
|
|
||||||
ChangeScheduler();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Thread::CancelWait() {
|
void Thread::CancelWait() {
|
||||||
|
@ -205,9 +195,9 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
|
||||||
thread->name = std::move(name);
|
thread->name = std::move(name);
|
||||||
thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap();
|
thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap();
|
||||||
thread->owner_process = &owner_process;
|
thread->owner_process = &owner_process;
|
||||||
|
auto& scheduler = kernel.GlobalScheduler();
|
||||||
|
scheduler.AddThread(thread);
|
||||||
thread->tls_address = thread->owner_process->CreateTLSRegion();
|
thread->tls_address = thread->owner_process->CreateTLSRegion();
|
||||||
thread->scheduler = &system.Scheduler(processor_id);
|
|
||||||
thread->scheduler->AddThread(thread);
|
|
||||||
|
|
||||||
thread->owner_process->RegisterThread(thread.get());
|
thread->owner_process->RegisterThread(thread.get());
|
||||||
|
|
||||||
|
@ -250,6 +240,22 @@ void Thread::SetStatus(ThreadStatus new_status) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (new_status) {
|
||||||
|
case ThreadStatus::Ready:
|
||||||
|
case ThreadStatus::Running:
|
||||||
|
SetSchedulingStatus(ThreadSchedStatus::Runnable);
|
||||||
|
break;
|
||||||
|
case ThreadStatus::Dormant:
|
||||||
|
SetSchedulingStatus(ThreadSchedStatus::None);
|
||||||
|
break;
|
||||||
|
case ThreadStatus::Dead:
|
||||||
|
SetSchedulingStatus(ThreadSchedStatus::Exited);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
SetSchedulingStatus(ThreadSchedStatus::Paused);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (status == ThreadStatus::Running) {
|
if (status == ThreadStatus::Running) {
|
||||||
last_running_ticks = Core::System::GetInstance().CoreTiming().GetTicks();
|
last_running_ticks = Core::System::GetInstance().CoreTiming().GetTicks();
|
||||||
}
|
}
|
||||||
|
@ -311,8 +317,7 @@ void Thread::UpdatePriority() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
scheduler->SetThreadPriority(this, new_priority);
|
SetCurrentPriority(new_priority);
|
||||||
current_priority = new_priority;
|
|
||||||
|
|
||||||
if (!lock_owner) {
|
if (!lock_owner) {
|
||||||
return;
|
return;
|
||||||
|
@ -328,47 +333,7 @@ void Thread::UpdatePriority() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Thread::ChangeCore(u32 core, u64 mask) {
|
void Thread::ChangeCore(u32 core, u64 mask) {
|
||||||
ideal_core = core;
|
SetCoreAndAffinityMask(core, mask);
|
||||||
affinity_mask = mask;
|
|
||||||
ChangeScheduler();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Thread::ChangeScheduler() {
|
|
||||||
if (status != ThreadStatus::Ready) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& system = Core::System::GetInstance();
|
|
||||||
std::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)};
|
|
||||||
|
|
||||||
if (!new_processor_id) {
|
|
||||||
new_processor_id = processor_id;
|
|
||||||
}
|
|
||||||
if (ideal_core != -1 && system.Scheduler(ideal_core).GetCurrentThread() == nullptr) {
|
|
||||||
new_processor_id = ideal_core;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT(*new_processor_id < 4);
|
|
||||||
|
|
||||||
// Add thread to new core's scheduler
|
|
||||||
auto& next_scheduler = system.Scheduler(*new_processor_id);
|
|
||||||
|
|
||||||
if (*new_processor_id != processor_id) {
|
|
||||||
// Remove thread from previous core's scheduler
|
|
||||||
scheduler->RemoveThread(this);
|
|
||||||
next_scheduler.AddThread(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
processor_id = *new_processor_id;
|
|
||||||
|
|
||||||
// If the thread was ready, unschedule from the previous core and schedule on the new core
|
|
||||||
scheduler->UnscheduleThread(this, current_priority);
|
|
||||||
next_scheduler.ScheduleThread(this, current_priority);
|
|
||||||
|
|
||||||
// Change thread's scheduler
|
|
||||||
scheduler = &next_scheduler;
|
|
||||||
|
|
||||||
system.CpuCore(processor_id).PrepareReschedule();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Thread::AllWaitObjectsReady() const {
|
bool Thread::AllWaitObjectsReady() const {
|
||||||
|
@ -391,7 +356,7 @@ void Thread::SetActivity(ThreadActivity value) {
|
||||||
if (status == ThreadStatus::Ready) {
|
if (status == ThreadStatus::Ready) {
|
||||||
status = ThreadStatus::Paused;
|
status = ThreadStatus::Paused;
|
||||||
} else if (status == ThreadStatus::Running) {
|
} else if (status == ThreadStatus::Running) {
|
||||||
status = ThreadStatus::Paused;
|
SetStatus(ThreadStatus::Paused);
|
||||||
Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
|
Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
|
||||||
}
|
}
|
||||||
} else if (status == ThreadStatus::Paused) {
|
} else if (status == ThreadStatus::Paused) {
|
||||||
|
@ -408,6 +373,165 @@ void Thread::Sleep(s64 nanoseconds) {
|
||||||
WakeAfterDelay(nanoseconds);
|
WakeAfterDelay(nanoseconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Thread::YieldType0() {
|
||||||
|
auto& scheduler = kernel.GlobalScheduler();
|
||||||
|
scheduler.YieldThread(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thread::YieldType1() {
|
||||||
|
auto& scheduler = kernel.GlobalScheduler();
|
||||||
|
scheduler.YieldThreadAndBalanceLoad(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thread::YieldType2() {
|
||||||
|
auto& scheduler = kernel.GlobalScheduler();
|
||||||
|
scheduler.YieldThreadAndWaitForLoadBalancing(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thread::SetSchedulingStatus(ThreadSchedStatus new_status) {
|
||||||
|
u32 old_flags = scheduling_state;
|
||||||
|
scheduling_state =
|
||||||
|
(scheduling_state & ThreadSchedMasks::HighMask) | static_cast<u32>(new_status);
|
||||||
|
AdjustSchedulingOnStatus(old_flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thread::SetCurrentPriority(u32 new_priority) {
|
||||||
|
u32 old_priority = current_priority;
|
||||||
|
current_priority = new_priority;
|
||||||
|
AdjustSchedulingOnPriority(old_priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) {
|
||||||
|
auto HighestSetCore = [](u64 mask, u32 max_cores) {
|
||||||
|
for (s32 core = max_cores - 1; core >= 0; core--) {
|
||||||
|
if (((mask >> core) & 1) != 0)
|
||||||
|
return core;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
bool use_override = affinity_override_count != 0;
|
||||||
|
// The value -3 is "do not change the ideal core".
|
||||||
|
if (new_core == -3) {
|
||||||
|
new_core = use_override ? ideal_core_override : ideal_core;
|
||||||
|
if ((new_affinity_mask & (1 << new_core)) == 0) {
|
||||||
|
return ERR_INVALID_COMBINATION;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (use_override) {
|
||||||
|
ideal_core_override = new_core;
|
||||||
|
affinity_mask_override = new_affinity_mask;
|
||||||
|
} else {
|
||||||
|
u64 old_affinity_mask = affinity_mask;
|
||||||
|
ideal_core = new_core;
|
||||||
|
affinity_mask = new_affinity_mask;
|
||||||
|
if (old_affinity_mask != new_affinity_mask) {
|
||||||
|
s32 old_core = processor_id;
|
||||||
|
if (processor_id >= 0 && ((affinity_mask >> processor_id) & 1) == 0) {
|
||||||
|
if (ideal_core < 0) {
|
||||||
|
processor_id = HighestSetCore(affinity_mask, GlobalScheduler::NUM_CPU_CORES);
|
||||||
|
} else {
|
||||||
|
processor_id = ideal_core;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AdjustSchedulingOnAffinity(old_affinity_mask, old_core);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return RESULT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thread::AdjustSchedulingOnStatus(u32 old_flags) {
|
||||||
|
if (old_flags == scheduling_state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto& scheduler = kernel.GlobalScheduler();
|
||||||
|
if (static_cast<ThreadSchedStatus>(old_flags & ThreadSchedMasks::LowMask) ==
|
||||||
|
ThreadSchedStatus::Runnable) {
|
||||||
|
// In this case the thread was running, now it's pausing/exitting
|
||||||
|
if (processor_id >= 0)
|
||||||
|
scheduler.Unschedule(current_priority, processor_id, this);
|
||||||
|
|
||||||
|
for (s32 core = 0; core < GlobalScheduler::NUM_CPU_CORES; core++) {
|
||||||
|
if (core != processor_id && ((affinity_mask >> core) & 1) != 0)
|
||||||
|
scheduler.Unsuggest(current_priority, core, this);
|
||||||
|
}
|
||||||
|
} else if (GetSchedulingStatus() == ThreadSchedStatus::Runnable) {
|
||||||
|
// The thread is now set to running from being stopped
|
||||||
|
if (processor_id >= 0)
|
||||||
|
scheduler.Schedule(current_priority, processor_id, this);
|
||||||
|
|
||||||
|
for (s32 core = 0; core < GlobalScheduler::NUM_CPU_CORES; core++) {
|
||||||
|
if (core != processor_id && ((affinity_mask >> core) & 1) != 0)
|
||||||
|
scheduler.Suggest(current_priority, core, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scheduler.SetReselectionPending();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thread::AdjustSchedulingOnPriority(u32 old_priority) {
|
||||||
|
if (GetSchedulingStatus() != ThreadSchedStatus::Runnable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto& scheduler = Core::System::GetInstance().GlobalScheduler();
|
||||||
|
if (processor_id >= 0) {
|
||||||
|
scheduler.Unschedule(old_priority, processor_id, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (s32 core = 0; core < GlobalScheduler::NUM_CPU_CORES; core++) {
|
||||||
|
if (core != processor_id && ((affinity_mask >> core) & 1) != 0) {
|
||||||
|
scheduler.Unsuggest(old_priority, core, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add thread to the new priority queues.
|
||||||
|
Thread* current_thread = GetCurrentThread();
|
||||||
|
|
||||||
|
if (processor_id >= 0) {
|
||||||
|
if (current_thread == this) {
|
||||||
|
scheduler.SchedulePrepend(current_priority, processor_id, this);
|
||||||
|
} else {
|
||||||
|
scheduler.Schedule(current_priority, processor_id, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (s32 core = 0; core < GlobalScheduler::NUM_CPU_CORES; core++) {
|
||||||
|
if (core != processor_id && ((affinity_mask >> core) & 1) != 0) {
|
||||||
|
scheduler.Suggest(current_priority, core, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scheduler.SetReselectionPending();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thread::AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core) {
|
||||||
|
auto& scheduler = Core::System::GetInstance().GlobalScheduler();
|
||||||
|
if (GetSchedulingStatus() != ThreadSchedStatus::Runnable ||
|
||||||
|
current_priority >= THREADPRIO_COUNT)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (s32 core = 0; core < GlobalScheduler::NUM_CPU_CORES; core++) {
|
||||||
|
if (((old_affinity_mask >> core) & 1) != 0) {
|
||||||
|
if (core == old_core) {
|
||||||
|
scheduler.Unschedule(current_priority, core, this);
|
||||||
|
} else {
|
||||||
|
scheduler.Unsuggest(current_priority, core, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (s32 core = 0; core < GlobalScheduler::NUM_CPU_CORES; core++) {
|
||||||
|
if (((affinity_mask >> core) & 1) != 0) {
|
||||||
|
if (core == processor_id) {
|
||||||
|
scheduler.Schedule(current_priority, core, this);
|
||||||
|
} else {
|
||||||
|
scheduler.Suggest(current_priority, core, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scheduler.SetReselectionPending();
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -75,6 +75,21 @@ enum class ThreadActivity : u32 {
|
||||||
Paused = 1,
|
Paused = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class ThreadSchedStatus : u32 { None = 0, Paused = 1, Runnable = 2, Exited = 3 };
|
||||||
|
|
||||||
|
enum ThreadSchedFlags : u32 {
|
||||||
|
ProcessPauseFlag = 1 << 4,
|
||||||
|
ThreadPauseFlag = 1 << 5,
|
||||||
|
ProcessDebugPauseFlag = 1 << 6,
|
||||||
|
KernelInitPauseFlag = 1 << 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ThreadSchedMasks : u32 {
|
||||||
|
LowMask = 0x000f,
|
||||||
|
HighMask = 0xfff0,
|
||||||
|
ForcePauseMask = 0x0070,
|
||||||
|
};
|
||||||
|
|
||||||
class Thread final : public WaitObject {
|
class Thread final : public WaitObject {
|
||||||
public:
|
public:
|
||||||
using MutexWaitingThreads = std::vector<SharedPtr<Thread>>;
|
using MutexWaitingThreads = std::vector<SharedPtr<Thread>>;
|
||||||
|
@ -278,6 +293,10 @@ public:
|
||||||
return processor_id;
|
return processor_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetProcessorID(s32 new_core) {
|
||||||
|
processor_id = new_core;
|
||||||
|
}
|
||||||
|
|
||||||
Process* GetOwnerProcess() {
|
Process* GetOwnerProcess() {
|
||||||
return owner_process;
|
return owner_process;
|
||||||
}
|
}
|
||||||
|
@ -383,11 +402,38 @@ public:
|
||||||
/// Sleeps this thread for the given amount of nanoseconds.
|
/// Sleeps this thread for the given amount of nanoseconds.
|
||||||
void Sleep(s64 nanoseconds);
|
void Sleep(s64 nanoseconds);
|
||||||
|
|
||||||
|
/// Yields this thread without rebalancing loads.
|
||||||
|
void YieldType0();
|
||||||
|
|
||||||
|
/// Yields this thread and does a load rebalancing.
|
||||||
|
void YieldType1();
|
||||||
|
|
||||||
|
/// Yields this thread and if the core is left idle, loads are rebalanced
|
||||||
|
void YieldType2();
|
||||||
|
|
||||||
|
ThreadSchedStatus GetSchedulingStatus() {
|
||||||
|
return static_cast<ThreadSchedStatus>(scheduling_state & ThreadSchedMasks::LowMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsRunning() const {
|
||||||
|
return is_running;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetIsRunning(bool value) {
|
||||||
|
is_running = value;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit Thread(KernelCore& kernel);
|
explicit Thread(KernelCore& kernel);
|
||||||
~Thread() override;
|
~Thread() override;
|
||||||
|
|
||||||
void ChangeScheduler();
|
void SetSchedulingStatus(ThreadSchedStatus new_status);
|
||||||
|
void SetCurrentPriority(u32 new_priority);
|
||||||
|
ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask);
|
||||||
|
|
||||||
|
void AdjustSchedulingOnStatus(u32 old_flags);
|
||||||
|
void AdjustSchedulingOnPriority(u32 old_priority);
|
||||||
|
void AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core);
|
||||||
|
|
||||||
Core::ARM_Interface::ThreadContext context{};
|
Core::ARM_Interface::ThreadContext context{};
|
||||||
|
|
||||||
|
@ -453,6 +499,13 @@ private:
|
||||||
|
|
||||||
ThreadActivity activity = ThreadActivity::Normal;
|
ThreadActivity activity = ThreadActivity::Normal;
|
||||||
|
|
||||||
|
s32 ideal_core_override = -1;
|
||||||
|
u64 affinity_mask_override = 0x1;
|
||||||
|
u32 affinity_override_count = 0;
|
||||||
|
|
||||||
|
u32 scheduling_state = 0;
|
||||||
|
bool is_running = false;
|
||||||
|
|
||||||
std::string name;
|
std::string name;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue