Addapt thread class to the new Scheduler
This commit is contained in:
		
							parent
							
								
									b164d8ee53
								
							
						
					
					
						commit
						a1ac0c6cb4
					
				| @ -45,15 +45,7 @@ void Thread::Stop() { | ||||
|                                                              callback_handle); | ||||
|     kernel.ThreadWakeupCallbackHandleTable().Close(callback_handle); | ||||
|     callback_handle = 0; | ||||
| 
 | ||||
|     // 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; | ||||
| 
 | ||||
|     SetStatus(ThreadStatus::Dead); | ||||
|     WakeupAllWaitingThreads(); | ||||
| 
 | ||||
|     // Clean up any dangling references in objects that this thread was waiting for
 | ||||
| @ -132,13 +124,11 @@ void Thread::ResumeFromWait() { | ||||
|     wakeup_callback = nullptr; | ||||
| 
 | ||||
|     if (activity == ThreadActivity::Paused) { | ||||
|         status = ThreadStatus::Paused; | ||||
|         SetStatus(ThreadStatus::Paused); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     status = ThreadStatus::Ready; | ||||
| 
 | ||||
|     ChangeScheduler(); | ||||
|     SetStatus(ThreadStatus::Ready); | ||||
| } | ||||
| 
 | ||||
| void Thread::CancelWait() { | ||||
| @ -205,9 +195,9 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name | ||||
|     thread->name = std::move(name); | ||||
|     thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap(); | ||||
|     thread->owner_process = &owner_process; | ||||
|     auto& scheduler = kernel.GlobalScheduler(); | ||||
|     scheduler.AddThread(thread); | ||||
|     thread->tls_address = thread->owner_process->CreateTLSRegion(); | ||||
|     thread->scheduler = &system.Scheduler(processor_id); | ||||
|     thread->scheduler->AddThread(thread); | ||||
| 
 | ||||
|     thread->owner_process->RegisterThread(thread.get()); | ||||
| 
 | ||||
| @ -250,6 +240,22 @@ void Thread::SetStatus(ThreadStatus new_status) { | ||||
|         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) { | ||||
|         last_running_ticks = Core::System::GetInstance().CoreTiming().GetTicks(); | ||||
|     } | ||||
| @ -311,8 +317,7 @@ void Thread::UpdatePriority() { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     scheduler->SetThreadPriority(this, new_priority); | ||||
|     current_priority = new_priority; | ||||
|     SetCurrentPriority(new_priority); | ||||
| 
 | ||||
|     if (!lock_owner) { | ||||
|         return; | ||||
| @ -328,47 +333,7 @@ void Thread::UpdatePriority() { | ||||
| } | ||||
| 
 | ||||
| void Thread::ChangeCore(u32 core, u64 mask) { | ||||
|     ideal_core = core; | ||||
|     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(); | ||||
|     SetCoreAndAffinityMask(core, mask); | ||||
| } | ||||
| 
 | ||||
| bool Thread::AllWaitObjectsReady() const { | ||||
| @ -391,7 +356,7 @@ void Thread::SetActivity(ThreadActivity value) { | ||||
|         if (status == ThreadStatus::Ready) { | ||||
|             status = ThreadStatus::Paused; | ||||
|         } else if (status == ThreadStatus::Running) { | ||||
|             status = ThreadStatus::Paused; | ||||
|             SetStatus(ThreadStatus::Paused); | ||||
|             Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule(); | ||||
|         } | ||||
|     } else if (status == ThreadStatus::Paused) { | ||||
| @ -408,6 +373,165 @@ void Thread::Sleep(s64 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, | ||||
| }; | ||||
| 
 | ||||
| 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 { | ||||
| public: | ||||
|     using MutexWaitingThreads = std::vector<SharedPtr<Thread>>; | ||||
| @ -278,6 +293,10 @@ public: | ||||
|         return processor_id; | ||||
|     } | ||||
| 
 | ||||
|     void SetProcessorID(s32 new_core) { | ||||
|         processor_id = new_core; | ||||
|     } | ||||
| 
 | ||||
|     Process* GetOwnerProcess() { | ||||
|         return owner_process; | ||||
|     } | ||||
| @ -383,11 +402,38 @@ public: | ||||
|     /// Sleeps this thread for the given amount of 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: | ||||
|     explicit Thread(KernelCore& kernel); | ||||
|     ~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{}; | ||||
| 
 | ||||
| @ -453,6 +499,13 @@ private: | ||||
| 
 | ||||
|     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; | ||||
| }; | ||||
| 
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Fernando Sahmkow
						Fernando Sahmkow