| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783 |
- // Copyright 2018 yuzu emulator team
- // Licensed under GPLv2 or any later version
- // Refer to the license.txt file included.
- //
- // SelectThreads, Yield functions originally by TuxSH.
- // licensed under GPLv2 or later under exception provided by the author.
- #include <algorithm>
- #include <set>
- #include <unordered_set>
- #include <utility>
- #include "common/assert.h"
- #include "common/bit_util.h"
- #include "common/fiber.h"
- #include "common/logging/log.h"
- #include "core/arm/arm_interface.h"
- #include "core/core.h"
- #include "core/core_timing.h"
- #include "core/cpu_manager.h"
- #include "core/hle/kernel/kernel.h"
- #include "core/hle/kernel/physical_core.h"
- #include "core/hle/kernel/process.h"
- #include "core/hle/kernel/scheduler.h"
- #include "core/hle/kernel/time_manager.h"
- namespace Kernel {
- GlobalScheduler::GlobalScheduler(KernelCore& kernel) : kernel{kernel} {}
- GlobalScheduler::~GlobalScheduler() = default;
- void GlobalScheduler::AddThread(std::shared_ptr<Thread> thread) {
- global_list_guard.lock();
- thread_list.push_back(std::move(thread));
- global_list_guard.unlock();
- }
- void GlobalScheduler::RemoveThread(std::shared_ptr<Thread> thread) {
- global_list_guard.lock();
- thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
- thread_list.end());
- global_list_guard.unlock();
- }
- u32 GlobalScheduler::SelectThreads() {
- ASSERT(is_locked);
- const auto update_thread = [](Thread* thread, Scheduler& sched) {
- sched.guard.lock();
- if (thread != sched.selected_thread_set.get()) {
- if (thread == nullptr) {
- ++sched.idle_selection_count;
- }
- sched.selected_thread_set = SharedFrom(thread);
- }
- const bool reschedule_pending =
- sched.is_context_switch_pending || (sched.selected_thread_set != sched.current_thread);
- sched.is_context_switch_pending = reschedule_pending;
- std::atomic_thread_fence(std::memory_order_seq_cst);
- sched.guard.unlock();
- return reschedule_pending;
- };
- if (!is_reselection_pending.load()) {
- return 0;
- }
- std::array<Thread*, Core::Hardware::NUM_CPU_CORES> top_threads{};
- u32 idle_cores{};
- // Step 1: Get top thread in schedule queue.
- for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
- Thread* top_thread =
- scheduled_queue[core].empty() ? nullptr : scheduled_queue[core].front();
- if (top_thread != nullptr) {
- // TODO(Blinkhawk): Implement Thread Pinning
- } else {
- idle_cores |= (1ul << core);
- }
- top_threads[core] = top_thread;
- }
- while (idle_cores != 0) {
- u32 core_id = Common::CountTrailingZeroes32(idle_cores);
- if (!suggested_queue[core_id].empty()) {
- std::array<s32, Core::Hardware::NUM_CPU_CORES> migration_candidates{};
- std::size_t num_candidates = 0;
- auto iter = suggested_queue[core_id].begin();
- Thread* suggested = nullptr;
- // Step 2: Try selecting a suggested thread.
- while (iter != suggested_queue[core_id].end()) {
- suggested = *iter;
- iter++;
- s32 suggested_core_id = suggested->GetProcessorID();
- Thread* top_thread =
- suggested_core_id > 0 ? top_threads[suggested_core_id] : nullptr;
- if (top_thread != suggested) {
- if (top_thread != nullptr &&
- top_thread->GetPriority() < THREADPRIO_MAX_CORE_MIGRATION) {
- suggested = nullptr;
- break;
- // There's a too high thread to do core migration, cancel
- }
- TransferToCore(suggested->GetPriority(), static_cast<s32>(core_id), suggested);
- break;
- }
- suggested = nullptr;
- migration_candidates[num_candidates++] = suggested_core_id;
- }
- // Step 3: Select a suggested thread from another core
- if (suggested == nullptr) {
- for (std::size_t i = 0; i < num_candidates; i++) {
- s32 candidate_core = migration_candidates[i];
- suggested = top_threads[candidate_core];
- auto it = scheduled_queue[candidate_core].begin();
- it++;
- Thread* next = it != scheduled_queue[candidate_core].end() ? *it : nullptr;
- if (next != nullptr) {
- TransferToCore(suggested->GetPriority(), static_cast<s32>(core_id),
- suggested);
- top_threads[candidate_core] = next;
- break;
- } else {
- suggested = nullptr;
- }
- }
- }
- top_threads[core_id] = suggested;
- }
- idle_cores &= ~(1ul << core_id);
- }
- u32 cores_needing_context_switch{};
- for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
- Scheduler& sched = kernel.Scheduler(core);
- if (update_thread(top_threads[core], sched)) {
- cores_needing_context_switch |= (1ul << core);
- }
- }
- return cores_needing_context_switch;
- }
- bool GlobalScheduler::YieldThread(Thread* yielding_thread) {
- ASSERT(is_locked);
- // Note: caller should use critical section, etc.
- const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID());
- const u32 priority = yielding_thread->GetPriority();
- // Yield the thread
- Reschedule(priority, core_id, yielding_thread);
- const Thread* const winner = scheduled_queue[core_id].front();
- if (kernel.GetCurrentHostThreadID() != core_id) {
- is_reselection_pending.store(true, std::memory_order_release);
- }
- return AskForReselectionOrMarkRedundant(yielding_thread, winner);
- }
- bool GlobalScheduler::YieldThreadAndBalanceLoad(Thread* yielding_thread) {
- ASSERT(is_locked);
- // Note: caller should check if !thread.IsSchedulerOperationRedundant and use critical section,
- // etc.
- const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID());
- const u32 priority = yielding_thread->GetPriority();
- // Yield the thread
- Reschedule(priority, core_id, yielding_thread);
- std::array<Thread*, Core::Hardware::NUM_CPU_CORES> current_threads;
- for (std::size_t i = 0; i < current_threads.size(); i++) {
- current_threads[i] = scheduled_queue[i].empty() ? nullptr : scheduled_queue[i].front();
- }
- Thread* next_thread = scheduled_queue[core_id].front(priority);
- Thread* winner = nullptr;
- for (auto& thread : suggested_queue[core_id]) {
- const s32 source_core = thread->GetProcessorID();
- if (source_core >= 0) {
- if (current_threads[source_core] != nullptr) {
- if (thread == current_threads[source_core] ||
- current_threads[source_core]->GetPriority() < min_regular_priority) {
- continue;
- }
- }
- }
- if (next_thread->GetLastRunningTicks() >= thread->GetLastRunningTicks() ||
- next_thread->GetPriority() < thread->GetPriority()) {
- if (thread->GetPriority() <= priority) {
- winner = thread;
- break;
- }
- }
- }
- if (winner != nullptr) {
- if (winner != yielding_thread) {
- TransferToCore(winner->GetPriority(), s32(core_id), winner);
- }
- } else {
- winner = next_thread;
- }
- if (kernel.GetCurrentHostThreadID() != core_id) {
- is_reselection_pending.store(true, std::memory_order_release);
- }
- return AskForReselectionOrMarkRedundant(yielding_thread, winner);
- }
- bool GlobalScheduler::YieldThreadAndWaitForLoadBalancing(Thread* yielding_thread) {
- ASSERT(is_locked);
- // Note: caller should check if !thread.IsSchedulerOperationRedundant and use critical section,
- // etc.
- Thread* winner = nullptr;
- const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID());
- // Remove the thread from its scheduled mlq, put it on the corresponding "suggested" one instead
- TransferToCore(yielding_thread->GetPriority(), -1, yielding_thread);
- // If the core is idle, perform load balancing, excluding the threads that have just used this
- // function...
- if (scheduled_queue[core_id].empty()) {
- // Here, "current_threads" is calculated after the ""yield"", unlike yield -1
- std::array<Thread*, Core::Hardware::NUM_CPU_CORES> current_threads;
- for (std::size_t i = 0; i < current_threads.size(); i++) {
- current_threads[i] = scheduled_queue[i].empty() ? nullptr : scheduled_queue[i].front();
- }
- for (auto& thread : suggested_queue[core_id]) {
- const s32 source_core = thread->GetProcessorID();
- if (source_core < 0 || thread == current_threads[source_core]) {
- continue;
- }
- if (current_threads[source_core] == nullptr ||
- current_threads[source_core]->GetPriority() >= min_regular_priority) {
- winner = thread;
- }
- break;
- }
- if (winner != nullptr) {
- if (winner != yielding_thread) {
- TransferToCore(winner->GetPriority(), static_cast<s32>(core_id), winner);
- }
- } else {
- winner = yielding_thread;
- }
- } else {
- winner = scheduled_queue[i].front();
- }
- if (kernel.GetCurrentHostThreadID() != core_id) {
- is_reselection_pending.store(true, std::memory_order_release);
- }
- return AskForReselectionOrMarkRedundant(yielding_thread, winner);
- }
- void GlobalScheduler::PreemptThreads() {
- ASSERT(is_locked);
- for (std::size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
- const u32 priority = preemption_priorities[core_id];
- if (scheduled_queue[core_id].size(priority) > 0) {
- if (scheduled_queue[core_id].size(priority) > 1) {
- scheduled_queue[core_id].front(priority)->IncrementYieldCount();
- }
- scheduled_queue[core_id].yield(priority);
- if (scheduled_queue[core_id].size(priority) > 1) {
- scheduled_queue[core_id].front(priority)->IncrementYieldCount();
- }
- }
- Thread* current_thread =
- scheduled_queue[core_id].empty() ? nullptr : scheduled_queue[core_id].front();
- Thread* winner = nullptr;
- for (auto& thread : suggested_queue[core_id]) {
- const s32 source_core = thread->GetProcessorID();
- if (thread->GetPriority() != priority) {
- continue;
- }
- if (source_core >= 0) {
- Thread* next_thread = scheduled_queue[source_core].empty()
- ? nullptr
- : scheduled_queue[source_core].front();
- if (next_thread != nullptr && next_thread->GetPriority() < 2) {
- break;
- }
- if (next_thread == thread) {
- continue;
- }
- }
- if (current_thread != nullptr &&
- current_thread->GetLastRunningTicks() >= thread->GetLastRunningTicks()) {
- winner = thread;
- break;
- }
- }
- if (winner != nullptr) {
- TransferToCore(winner->GetPriority(), s32(core_id), winner);
- current_thread =
- winner->GetPriority() <= current_thread->GetPriority() ? winner : current_thread;
- }
- if (current_thread != nullptr && current_thread->GetPriority() > priority) {
- for (auto& thread : suggested_queue[core_id]) {
- const s32 source_core = thread->GetProcessorID();
- if (thread->GetPriority() < priority) {
- continue;
- }
- if (source_core >= 0) {
- Thread* next_thread = scheduled_queue[source_core].empty()
- ? nullptr
- : scheduled_queue[source_core].front();
- if (next_thread != nullptr && next_thread->GetPriority() < 2) {
- break;
- }
- if (next_thread == thread) {
- continue;
- }
- }
- if (current_thread != nullptr &&
- current_thread->GetLastRunningTicks() >= thread->GetLastRunningTicks()) {
- winner = thread;
- break;
- }
- }
- if (winner != nullptr) {
- TransferToCore(winner->GetPriority(), s32(core_id), winner);
- current_thread = winner;
- }
- }
- is_reselection_pending.store(true, std::memory_order_release);
- }
- }
- void GlobalScheduler::EnableInterruptAndSchedule(u32 cores_pending_reschedule,
- Core::EmuThreadHandle global_thread) {
- u32 current_core = global_thread.host_handle;
- bool must_context_switch = global_thread.guest_handle != InvalidHandle &&
- (current_core < Core::Hardware::NUM_CPU_CORES);
- while (cores_pending_reschedule != 0) {
- u32 core = Common::CountTrailingZeroes32(cores_pending_reschedule);
- ASSERT(core < Core::Hardware::NUM_CPU_CORES);
- if (!must_context_switch || core != current_core) {
- auto& phys_core = kernel.PhysicalCore(core);
- phys_core.Interrupt();
- } else {
- must_context_switch = true;
- }
- cores_pending_reschedule &= ~(1ul << core);
- }
- if (must_context_switch) {
- auto& core_scheduler = kernel.CurrentScheduler();
- core_scheduler.TryDoContextSwitch();
- }
- }
- void GlobalScheduler::Suggest(u32 priority, std::size_t core, Thread* thread) {
- ASSERT(is_locked);
- suggested_queue[core].add(thread, priority);
- }
- void GlobalScheduler::Unsuggest(u32 priority, std::size_t core, Thread* thread) {
- ASSERT(is_locked);
- suggested_queue[core].remove(thread, priority);
- }
- void GlobalScheduler::Schedule(u32 priority, std::size_t core, Thread* thread) {
- ASSERT(is_locked);
- ASSERT_MSG(thread->GetProcessorID() == s32(core), "Thread must be assigned to this core.");
- scheduled_queue[core].add(thread, priority);
- }
- void GlobalScheduler::SchedulePrepend(u32 priority, std::size_t core, Thread* thread) {
- ASSERT(is_locked);
- ASSERT_MSG(thread->GetProcessorID() == s32(core), "Thread must be assigned to this core.");
- scheduled_queue[core].add(thread, priority, false);
- }
- void GlobalScheduler::Reschedule(u32 priority, std::size_t core, Thread* thread) {
- ASSERT(is_locked);
- scheduled_queue[core].remove(thread, priority);
- scheduled_queue[core].add(thread, priority);
- }
- void GlobalScheduler::Unschedule(u32 priority, std::size_t core, Thread* thread) {
- ASSERT(is_locked);
- scheduled_queue[core].remove(thread, priority);
- }
- void GlobalScheduler::TransferToCore(u32 priority, s32 destination_core, Thread* thread) {
- ASSERT(is_locked);
- const bool schedulable = thread->GetPriority() < THREADPRIO_COUNT;
- const s32 source_core = thread->GetProcessorID();
- if (source_core == destination_core || !schedulable) {
- return;
- }
- thread->SetProcessorID(destination_core);
- if (source_core >= 0) {
- Unschedule(priority, static_cast<u32>(source_core), thread);
- }
- if (destination_core >= 0) {
- Unsuggest(priority, static_cast<u32>(destination_core), thread);
- Schedule(priority, static_cast<u32>(destination_core), thread);
- }
- if (source_core >= 0) {
- Suggest(priority, static_cast<u32>(source_core), thread);
- }
- }
- bool GlobalScheduler::AskForReselectionOrMarkRedundant(Thread* current_thread,
- const Thread* winner) {
- if (current_thread == winner) {
- current_thread->IncrementYieldCount();
- return true;
- } else {
- is_reselection_pending.store(true, std::memory_order_release);
- return false;
- }
- }
- void GlobalScheduler::AdjustSchedulingOnStatus(Thread* thread, u32 old_flags) {
- if (old_flags == thread->scheduling_state) {
- return;
- }
- ASSERT(is_locked);
- if (old_flags == static_cast<u32>(ThreadSchedStatus::Runnable)) {
- // In this case the thread was running, now it's pausing/exitting
- if (thread->processor_id >= 0) {
- Unschedule(thread->current_priority, static_cast<u32>(thread->processor_id), thread);
- }
- for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
- if (core != static_cast<u32>(thread->processor_id) &&
- ((thread->affinity_mask >> core) & 1) != 0) {
- Unsuggest(thread->current_priority, core, thread);
- }
- }
- } else if (thread->scheduling_state == static_cast<u32>(ThreadSchedStatus::Runnable)) {
- // The thread is now set to running from being stopped
- if (thread->processor_id >= 0) {
- Schedule(thread->current_priority, static_cast<u32>(thread->processor_id), thread);
- }
- for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
- if (core != static_cast<u32>(thread->processor_id) &&
- ((thread->affinity_mask >> core) & 1) != 0) {
- Suggest(thread->current_priority, core, thread);
- }
- }
- }
- SetReselectionPending();
- }
- void GlobalScheduler::AdjustSchedulingOnPriority(Thread* thread, u32 old_priority) {
- if (thread->scheduling_state != static_cast<u32>(ThreadSchedStatus::Runnable)) {
- return;
- }
- ASSERT(is_locked);
- if (thread->processor_id >= 0) {
- Unschedule(old_priority, static_cast<u32>(thread->processor_id), thread);
- }
- for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
- if (core != static_cast<u32>(thread->processor_id) &&
- ((thread->affinity_mask >> core) & 1) != 0) {
- Unsuggest(old_priority, core, thread);
- }
- }
- if (thread->processor_id >= 0) {
- if (thread == kernel.CurrentScheduler().GetCurrentThread()) {
- SchedulePrepend(thread->current_priority, static_cast<u32>(thread->processor_id),
- thread);
- } else {
- Schedule(thread->current_priority, static_cast<u32>(thread->processor_id), thread);
- }
- }
- for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
- if (core != static_cast<u32>(thread->processor_id) &&
- ((thread->affinity_mask >> core) & 1) != 0) {
- Suggest(thread->current_priority, core, thread);
- }
- }
- thread->IncrementYieldCount();
- SetReselectionPending();
- }
- void GlobalScheduler::AdjustSchedulingOnAffinity(Thread* thread, u64 old_affinity_mask,
- s32 old_core) {
- if (thread->scheduling_state != static_cast<u32>(ThreadSchedStatus::Runnable) ||
- thread->current_priority >= THREADPRIO_COUNT) {
- return;
- }
- ASSERT(is_locked);
- for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
- if (((old_affinity_mask >> core) & 1) != 0) {
- if (core == static_cast<u32>(old_core)) {
- Unschedule(thread->current_priority, core, thread);
- } else {
- Unsuggest(thread->current_priority, core, thread);
- }
- }
- }
- for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
- if (((thread->affinity_mask >> core) & 1) != 0) {
- if (core == static_cast<u32>(thread->processor_id)) {
- Schedule(thread->current_priority, core, thread);
- } else {
- Suggest(thread->current_priority, core, thread);
- }
- }
- }
- thread->IncrementYieldCount();
- SetReselectionPending();
- }
- void GlobalScheduler::Shutdown() {
- for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
- scheduled_queue[core].clear();
- suggested_queue[core].clear();
- }
- thread_list.clear();
- }
- void GlobalScheduler::Lock() {
- Core::EmuThreadHandle current_thread = kernel.GetCurrentEmuThreadID();
- ASSERT(!current_thread.IsInvalid());
- if (current_thread == current_owner) {
- ++scope_lock;
- } else {
- inner_lock.lock();
- is_locked = true;
- current_owner = current_thread;
- ASSERT(current_owner != Core::EmuThreadHandle::InvalidHandle());
- scope_lock = 1;
- }
- }
- void GlobalScheduler::Unlock() {
- if (--scope_lock != 0) {
- ASSERT(scope_lock > 0);
- return;
- }
- u32 cores_pending_reschedule = SelectThreads();
- Core::EmuThreadHandle leaving_thread = current_owner;
- current_owner = Core::EmuThreadHandle::InvalidHandle();
- scope_lock = 1;
- is_locked = false;
- inner_lock.unlock();
- EnableInterruptAndSchedule(cores_pending_reschedule, leaving_thread);
- }
- Scheduler::Scheduler(Core::System& system, std::size_t core_id) : system(system), core_id(core_id) {
- switch_fiber = std::make_shared<Common::Fiber>(std::function<void(void*)>(OnSwitch), this);
- }
- Scheduler::~Scheduler() = default;
- bool Scheduler::HaveReadyThreads() const {
- return system.GlobalScheduler().HaveReadyThreads(core_id);
- }
- Thread* Scheduler::GetCurrentThread() const {
- if (current_thread) {
- return current_thread.get();
- }
- return idle_thread.get();
- }
- Thread* Scheduler::GetSelectedThread() const {
- return selected_thread.get();
- }
- u64 Scheduler::GetLastContextSwitchTicks() const {
- return last_context_switch_time;
- }
- void Scheduler::TryDoContextSwitch() {
- auto& phys_core = system.Kernel().CurrentPhysicalCore();
- if (phys_core.IsInterrupted()) {
- phys_core.ClearInterrupt();
- }
- guard.lock();
- if (is_context_switch_pending) {
- SwitchContext();
- } else {
- guard.unlock();
- }
- }
- void Scheduler::OnThreadStart() {
- SwitchContextStep2();
- }
- void Scheduler::SwitchContextStep2() {
- Thread* previous_thread = current_thread_prev.get();
- Thread* new_thread = selected_thread.get();
- // Load context of new thread
- Process* const previous_process =
- previous_thread != nullptr ? previous_thread->GetOwnerProcess() : nullptr;
- if (new_thread) {
- ASSERT_MSG(new_thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable,
- "Thread must be runnable.");
- // Cancel any outstanding wakeup events for this thread
- new_thread->SetIsRunning(true);
- auto* const thread_owner_process = current_thread->GetOwnerProcess();
- if (previous_process != thread_owner_process && thread_owner_process != nullptr) {
- system.Kernel().MakeCurrentProcess(thread_owner_process);
- }
- if (!new_thread->IsHLEThread()) {
- auto& cpu_core = system.ArmInterface(core_id);
- cpu_core.LoadContext(new_thread->GetContext32());
- cpu_core.LoadContext(new_thread->GetContext64());
- cpu_core.SetTlsAddress(new_thread->GetTLSAddress());
- cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0());
- cpu_core.ClearExclusiveState();
- }
- }
- TryDoContextSwitch();
- }
- void Scheduler::SwitchContext() {
- current_thread_prev = current_thread;
- selected_thread = selected_thread_set;
- Thread* previous_thread = current_thread_prev.get();
- Thread* new_thread = selected_thread.get();
- current_thread = selected_thread;
- is_context_switch_pending = false;
- if (new_thread == previous_thread) {
- guard.unlock();
- return;
- }
- Process* const previous_process = system.Kernel().CurrentProcess();
- UpdateLastContextSwitchTime(previous_thread, previous_process);
- // Save context for previous thread
- if (previous_thread) {
- if (!previous_thread->IsHLEThread()) {
- auto& cpu_core = system.ArmInterface(core_id);
- cpu_core.SaveContext(previous_thread->GetContext32());
- cpu_core.SaveContext(previous_thread->GetContext64());
- // Save the TPIDR_EL0 system register in case it was modified.
- previous_thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
- cpu_core.ClearExclusiveState();
- }
- if (previous_thread->GetStatus() == ThreadStatus::Running) {
- previous_thread->SetStatus(ThreadStatus::Ready);
- }
- previous_thread->SetIsRunning(false);
- previous_thread->context_guard.unlock();
- }
- std::shared_ptr<Common::Fiber> old_context;
- if (previous_thread != nullptr) {
- old_context = previous_thread->GetHostContext();
- } else {
- old_context = idle_thread->GetHostContext();
- }
- guard.unlock();
- Common::Fiber::YieldTo(old_context, switch_fiber);
- /// When a thread wakes up, the scheduler may have changed to other in another core.
- auto& next_scheduler = system.Kernel().CurrentScheduler();
- next_scheduler.SwitchContextStep2();
- }
- void Scheduler::OnSwitch(void* this_scheduler) {
- Scheduler* sched = static_cast<Scheduler*>(this_scheduler);
- sched->SwitchToCurrent();
- }
- void Scheduler::SwitchToCurrent() {
- while (true) {
- guard.lock();
- selected_thread = selected_thread_set;
- current_thread = selected_thread;
- is_context_switch_pending = false;
- guard.unlock();
- while (!is_context_switch_pending) {
- if (current_thread != nullptr && !current_thread->IsHLEThread()) {
- current_thread->context_guard.lock();
- if (!current_thread->IsRunnable()) {
- current_thread->context_guard.unlock();
- break;
- }
- if (current_thread->GetProcessorID() != core_id) {
- current_thread->context_guard.unlock();
- break;
- }
- }
- std::shared_ptr<Common::Fiber> next_context;
- if (current_thread != nullptr) {
- next_context = current_thread->GetHostContext();
- } else {
- next_context = idle_thread->GetHostContext();
- }
- Common::Fiber::YieldTo(switch_fiber, next_context);
- }
- }
- }
- void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
- const u64 prev_switch_ticks = last_context_switch_time;
- const u64 most_recent_switch_ticks = system.CoreTiming().GetCPUTicks();
- const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
- if (thread != nullptr) {
- thread->UpdateCPUTimeTicks(update_ticks);
- }
- if (process != nullptr) {
- process->UpdateCPUTimeTicks(update_ticks);
- }
- last_context_switch_time = most_recent_switch_ticks;
- }
- void Scheduler::Initialize() {
- std::string name = "Idle Thread Id:" + std::to_string(core_id);
- std::function<void(void*)> init_func = system.GetCpuManager().GetIdleThreadStartFunc();
- void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
- ThreadType type = static_cast<ThreadType>(THREADTYPE_KERNEL | THREADTYPE_HLE | THREADTYPE_IDLE);
- auto thread_res = Thread::Create(system, type, name, 0, 64, 0, static_cast<u32>(core_id), 0,
- nullptr, std::move(init_func), init_func_parameter);
- idle_thread = std::move(thread_res).Unwrap();
- }
- void Scheduler::Shutdown() {
- current_thread = nullptr;
- selected_thread = nullptr;
- }
- SchedulerLock::SchedulerLock(KernelCore& kernel) : kernel{kernel} {
- kernel.GlobalScheduler().Lock();
- }
- SchedulerLock::~SchedulerLock() {
- kernel.GlobalScheduler().Unlock();
- }
- SchedulerLockAndSleep::SchedulerLockAndSleep(KernelCore& kernel, Handle& event_handle,
- Thread* time_task, s64 nanoseconds)
- : SchedulerLock{kernel}, event_handle{event_handle}, time_task{time_task}, nanoseconds{
- nanoseconds} {
- event_handle = InvalidHandle;
- }
- SchedulerLockAndSleep::~SchedulerLockAndSleep() {
- if (sleep_cancelled) {
- return;
- }
- auto& time_manager = kernel.TimeManager();
- time_manager.ScheduleTimeEvent(event_handle, time_task, nanoseconds);
- }
- void SchedulerLockAndSleep::Release() {
- if (sleep_cancelled) {
- return;
- }
- auto& time_manager = kernel.TimeManager();
- time_manager.ScheduleTimeEvent(event_handle, time_task, nanoseconds);
- sleep_cancelled = true;
- }
- } // namespace Kernel
|