mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu-mainline.git
				synced 2025-03-21 01:53:15 +00:00 
			
		
		
		
	Merge pull request #202 from bunnei/scheduler-cleanup
Scheduler cleanup
This commit is contained in:
		
						commit
						23fe6f5be3
					
				| @ -28,8 +28,6 @@ add_library(core STATIC | ||||
|     hle/config_mem.h | ||||
|     hle/ipc.h | ||||
|     hle/ipc_helpers.h | ||||
|     hle/kernel/address_arbiter.cpp | ||||
|     hle/kernel/address_arbiter.h | ||||
|     hle/kernel/client_port.cpp | ||||
|     hle/kernel/client_port.h | ||||
|     hle/kernel/client_session.cpp | ||||
| @ -55,6 +53,8 @@ add_library(core STATIC | ||||
|     hle/kernel/process.h | ||||
|     hle/kernel/resource_limit.cpp | ||||
|     hle/kernel/resource_limit.h | ||||
|     hle/kernel/scheduler.cpp | ||||
|     hle/kernel/scheduler.h | ||||
|     hle/kernel/server_port.cpp | ||||
|     hle/kernel/server_port.h | ||||
|     hle/kernel/server_session.cpp | ||||
|  | ||||
| @ -133,7 +133,7 @@ void System::Reschedule() { | ||||
|     } | ||||
| 
 | ||||
|     reschedule_pending = false; | ||||
|     Kernel::Reschedule(); | ||||
|     Core::System::GetInstance().Scheduler().Reschedule(); | ||||
| } | ||||
| 
 | ||||
| System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { | ||||
| @ -141,19 +141,20 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { | ||||
| 
 | ||||
|     switch (Settings::values.cpu_core) { | ||||
|     case Settings::CpuCore::Unicorn: | ||||
|         cpu_core = std::make_unique<ARM_Unicorn>(); | ||||
|         cpu_core = std::make_shared<ARM_Unicorn>(); | ||||
|         break; | ||||
|     case Settings::CpuCore::Dynarmic: | ||||
|     default: | ||||
| #ifdef ARCHITECTURE_x86_64 | ||||
|         cpu_core = std::make_unique<ARM_Dynarmic>(); | ||||
|         cpu_core = std::make_shared<ARM_Dynarmic>(); | ||||
| #else | ||||
|         cpu_core = std::make_unique<ARM_Unicorn>(); | ||||
|         cpu_core = std::make_shared<ARM_Unicorn>(); | ||||
|         LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); | ||||
| #endif | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     scheduler = std::make_unique<Kernel::Scheduler>(cpu_core.get()); | ||||
|     gpu_core = std::make_unique<Tegra::GPU>(); | ||||
| 
 | ||||
|     telemetry_session = std::make_unique<Core::TelemetrySession>(); | ||||
|  | ||||
| @ -7,6 +7,7 @@ | ||||
| #include <memory> | ||||
| #include <string> | ||||
| #include "common/common_types.h" | ||||
| #include "core/hle/kernel/scheduler.h" | ||||
| #include "core/loader/loader.h" | ||||
| #include "core/memory.h" | ||||
| #include "core/perf_stats.h" | ||||
| @ -107,6 +108,10 @@ public: | ||||
|         return *gpu_core; | ||||
|     } | ||||
| 
 | ||||
|     Kernel::Scheduler& Scheduler() { | ||||
|         return *scheduler; | ||||
|     } | ||||
| 
 | ||||
|     PerfStats perf_stats; | ||||
|     FrameLimiter frame_limiter; | ||||
| 
 | ||||
| @ -140,9 +145,8 @@ private: | ||||
|     /// AppLoader used to load the current executing application
 | ||||
|     std::unique_ptr<Loader::AppLoader> app_loader; | ||||
| 
 | ||||
|     ///< ARM11 CPU core
 | ||||
|     std::unique_ptr<ARM_Interface> cpu_core; | ||||
| 
 | ||||
|     std::shared_ptr<ARM_Interface> cpu_core; | ||||
|     std::unique_ptr<Kernel::Scheduler> scheduler; | ||||
|     std::unique_ptr<Tegra::GPU> gpu_core; | ||||
| 
 | ||||
|     /// When true, signals that a reschedule should happen
 | ||||
|  | ||||
| @ -1,91 +0,0 @@ | ||||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "core/hle/kernel/address_arbiter.h" | ||||
| #include "core/hle/kernel/errors.h" | ||||
| #include "core/hle/kernel/thread.h" | ||||
| #include "core/memory.h" | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Kernel namespace
 | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| AddressArbiter::AddressArbiter() {} | ||||
| AddressArbiter::~AddressArbiter() {} | ||||
| 
 | ||||
| SharedPtr<AddressArbiter> AddressArbiter::Create(std::string name) { | ||||
|     SharedPtr<AddressArbiter> address_arbiter(new AddressArbiter); | ||||
| 
 | ||||
|     address_arbiter->name = std::move(name); | ||||
| 
 | ||||
|     return address_arbiter; | ||||
| } | ||||
| 
 | ||||
| ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address, s32 value, | ||||
|                                             u64 nanoseconds) { | ||||
|     switch (type) { | ||||
| 
 | ||||
|     // Signal thread(s) waiting for arbitrate address...
 | ||||
|     case ArbitrationType::Signal: | ||||
|         // Negative value means resume all threads
 | ||||
|         if (value < 0) { | ||||
|             ArbitrateAllThreads(address); | ||||
|         } else { | ||||
|             // Resume first N threads
 | ||||
|             for (int i = 0; i < value; i++) | ||||
|                 ArbitrateHighestPriorityThread(address); | ||||
|         } | ||||
|         break; | ||||
| 
 | ||||
|     // Wait current thread (acquire the arbiter)...
 | ||||
|     case ArbitrationType::WaitIfLessThan: | ||||
|         if ((s32)Memory::Read32(address) < value) { | ||||
|             Kernel::WaitCurrentThread_ArbitrateAddress(address); | ||||
|         } | ||||
|         break; | ||||
|     case ArbitrationType::WaitIfLessThanWithTimeout: | ||||
|         if ((s32)Memory::Read32(address) < value) { | ||||
|             Kernel::WaitCurrentThread_ArbitrateAddress(address); | ||||
|             GetCurrentThread()->WakeAfterDelay(nanoseconds); | ||||
|         } | ||||
|         break; | ||||
|     case ArbitrationType::DecrementAndWaitIfLessThan: { | ||||
|         s32 memory_value = Memory::Read32(address); | ||||
|         if (memory_value < value) { | ||||
|             // Only change the memory value if the thread should wait
 | ||||
|             Memory::Write32(address, (s32)memory_value - 1); | ||||
|             Kernel::WaitCurrentThread_ArbitrateAddress(address); | ||||
|         } | ||||
|         break; | ||||
|     } | ||||
|     case ArbitrationType::DecrementAndWaitIfLessThanWithTimeout: { | ||||
|         s32 memory_value = Memory::Read32(address); | ||||
|         if (memory_value < value) { | ||||
|             // Only change the memory value if the thread should wait
 | ||||
|             Memory::Write32(address, (s32)memory_value - 1); | ||||
|             Kernel::WaitCurrentThread_ArbitrateAddress(address); | ||||
|             GetCurrentThread()->WakeAfterDelay(nanoseconds); | ||||
|         } | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     default: | ||||
|         LOG_ERROR(Kernel, "unknown type=%d", type); | ||||
|         return ERR_INVALID_ENUM_VALUE_FND; | ||||
|     } | ||||
| 
 | ||||
|     // The calls that use a timeout seem to always return a Timeout error even if they did not put
 | ||||
|     // the thread to sleep
 | ||||
|     if (type == ArbitrationType::WaitIfLessThanWithTimeout || | ||||
|         type == ArbitrationType::DecrementAndWaitIfLessThanWithTimeout) { | ||||
| 
 | ||||
|         return RESULT_TIMEOUT; | ||||
|     } | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| 
 | ||||
| } // namespace Kernel
 | ||||
| @ -1,60 +0,0 @@ | ||||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/result.h" | ||||
| 
 | ||||
| // Address arbiters are an underlying kernel synchronization object that can be created/used via
 | ||||
| // supervisor calls (SVCs). They function as sort of a global lock. Typically, games/other CTR
 | ||||
| // applications use them as an underlying mechanism to implement thread-safe barriers, events, and
 | ||||
| // semphores.
 | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Kernel namespace
 | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| enum class ArbitrationType : u32 { | ||||
|     Signal, | ||||
|     WaitIfLessThan, | ||||
|     DecrementAndWaitIfLessThan, | ||||
|     WaitIfLessThanWithTimeout, | ||||
|     DecrementAndWaitIfLessThanWithTimeout, | ||||
| }; | ||||
| 
 | ||||
| class AddressArbiter final : public Object { | ||||
| public: | ||||
|     /**
 | ||||
|      * Creates an address arbiter. | ||||
|      * | ||||
|      * @param name Optional name used for debugging. | ||||
|      * @returns The created AddressArbiter. | ||||
|      */ | ||||
|     static SharedPtr<AddressArbiter> Create(std::string name = "Unknown"); | ||||
| 
 | ||||
|     std::string GetTypeName() const override { | ||||
|         return "Arbiter"; | ||||
|     } | ||||
|     std::string GetName() const override { | ||||
|         return name; | ||||
|     } | ||||
| 
 | ||||
|     static const HandleType HANDLE_TYPE = HandleType::AddressArbiter; | ||||
|     HandleType GetHandleType() const override { | ||||
|         return HANDLE_TYPE; | ||||
|     } | ||||
| 
 | ||||
|     std::string name; ///< Name of address arbiter object (optional)
 | ||||
| 
 | ||||
|     ResultCode ArbitrateAddress(ArbitrationType type, VAddr address, s32 value, u64 nanoseconds); | ||||
| 
 | ||||
| private: | ||||
|     AddressArbiter(); | ||||
|     ~AddressArbiter() override; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Kernel
 | ||||
							
								
								
									
										134
									
								
								src/core/hle/kernel/scheduler.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								src/core/hle/kernel/scheduler.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,134 @@ | ||||
| // Copyright 2018 yuzu emulator team
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "core/core_timing.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/kernel/scheduler.h" | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| Scheduler::Scheduler(ARM_Interface* cpu_core) : cpu_core(cpu_core) {} | ||||
| 
 | ||||
| Scheduler::~Scheduler() { | ||||
|     for (auto& thread : thread_list) { | ||||
|         thread->Stop(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool Scheduler::HaveReadyThreads() { | ||||
|     return ready_queue.get_first() != nullptr; | ||||
| } | ||||
| 
 | ||||
| Thread* Scheduler::GetCurrentThread() const { | ||||
|     return current_thread.get(); | ||||
| } | ||||
| 
 | ||||
| Thread* Scheduler::PopNextReadyThread() { | ||||
|     Thread* next = nullptr; | ||||
|     Thread* thread = GetCurrentThread(); | ||||
| 
 | ||||
|     if (thread && thread->status == THREADSTATUS_RUNNING) { | ||||
|         // We have to do better than the current thread.
 | ||||
|         // This call returns null when that's not possible.
 | ||||
|         next = ready_queue.pop_first_better(thread->current_priority); | ||||
|         if (!next) { | ||||
|             // Otherwise just keep going with the current thread
 | ||||
|             next = thread; | ||||
|         } | ||||
|     } else { | ||||
|         next = ready_queue.pop_first(); | ||||
|     } | ||||
| 
 | ||||
|     return next; | ||||
| } | ||||
| 
 | ||||
| void Scheduler::SwitchContext(Thread* new_thread) { | ||||
|     Thread* previous_thread = GetCurrentThread(); | ||||
| 
 | ||||
|     // Save context for previous thread
 | ||||
|     if (previous_thread) { | ||||
|         previous_thread->last_running_ticks = CoreTiming::GetTicks(); | ||||
|         cpu_core->SaveContext(previous_thread->context); | ||||
| 
 | ||||
|         if (previous_thread->status == THREADSTATUS_RUNNING) { | ||||
|             // This is only the case when a reschedule is triggered without the current thread
 | ||||
|             // yielding execution (i.e. an event triggered, system core time-sliced, etc)
 | ||||
|             ready_queue.push_front(previous_thread->current_priority, previous_thread); | ||||
|             previous_thread->status = THREADSTATUS_READY; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Load context of new thread
 | ||||
|     if (new_thread) { | ||||
|         ASSERT_MSG(new_thread->status == THREADSTATUS_READY, | ||||
|                    "Thread must be ready to become running."); | ||||
| 
 | ||||
|         // Cancel any outstanding wakeup events for this thread
 | ||||
|         new_thread->CancelWakeupTimer(); | ||||
| 
 | ||||
|         auto previous_process = Kernel::g_current_process; | ||||
| 
 | ||||
|         current_thread = new_thread; | ||||
| 
 | ||||
|         ready_queue.remove(new_thread->current_priority, new_thread); | ||||
|         new_thread->status = THREADSTATUS_RUNNING; | ||||
| 
 | ||||
|         if (previous_process != current_thread->owner_process) { | ||||
|             Kernel::g_current_process = current_thread->owner_process; | ||||
|             SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table); | ||||
|         } | ||||
| 
 | ||||
|         cpu_core->LoadContext(new_thread->context); | ||||
|         cpu_core->SetTlsAddress(new_thread->GetTLSAddress()); | ||||
|     } else { | ||||
|         current_thread = nullptr; | ||||
|         // Note: We do not reset the current process and current page table when idling because
 | ||||
|         // technically we haven't changed processes, our threads are just paused.
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void Scheduler::Reschedule() { | ||||
|     Thread* cur = GetCurrentThread(); | ||||
|     Thread* next = PopNextReadyThread(); | ||||
| 
 | ||||
|     if (cur && next) { | ||||
|         LOG_TRACE(Kernel, "context switch %u -> %u", cur->GetObjectId(), next->GetObjectId()); | ||||
|     } else if (cur) { | ||||
|         LOG_TRACE(Kernel, "context switch %u -> idle", cur->GetObjectId()); | ||||
|     } else if (next) { | ||||
|         LOG_TRACE(Kernel, "context switch idle -> %u", next->GetObjectId()); | ||||
|     } | ||||
| 
 | ||||
|     SwitchContext(next); | ||||
| } | ||||
| 
 | ||||
| void Scheduler::AddThread(SharedPtr<Thread> thread, u32 priority) { | ||||
|     thread_list.push_back(thread); | ||||
|     ready_queue.prepare(priority); | ||||
| } | ||||
| 
 | ||||
| void Scheduler::RemoveThread(Thread* thread) { | ||||
|     thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread), | ||||
|                       thread_list.end()); | ||||
| } | ||||
| 
 | ||||
| void Scheduler::ScheduleThread(Thread* thread, u32 priority) { | ||||
|     ASSERT(thread->status == THREADSTATUS_READY); | ||||
|     ready_queue.push_back(priority, thread); | ||||
| } | ||||
| 
 | ||||
| void Scheduler::UnscheduleThread(Thread* thread, u32 priority) { | ||||
|     ASSERT(thread->status == THREADSTATUS_READY); | ||||
|     ready_queue.remove(priority, thread); | ||||
| } | ||||
| 
 | ||||
| void Scheduler::SetThreadPriority(Thread* thread, u32 priority) { | ||||
|     // If thread was ready, adjust queues
 | ||||
|     if (thread->status == THREADSTATUS_READY) | ||||
|         ready_queue.move(thread, thread->current_priority, priority); | ||||
|     else | ||||
|         ready_queue.prepare(priority); | ||||
| } | ||||
| 
 | ||||
| } // namespace Kernel
 | ||||
							
								
								
									
										73
									
								
								src/core/hle/kernel/scheduler.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/core/hle/kernel/scheduler.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,73 @@ | ||||
| // Copyright 2018 yuzu emulator team
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <vector> | ||||
| #include "common/common_types.h" | ||||
| #include "common/thread_queue_list.h" | ||||
| #include "core/arm/arm_interface.h" | ||||
| #include "core/hle/kernel/thread.h" | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| class Scheduler final { | ||||
| public: | ||||
|     explicit Scheduler(ARM_Interface* cpu_core); | ||||
|     ~Scheduler(); | ||||
| 
 | ||||
|     /// Returns whether there are any threads that are ready to run.
 | ||||
|     bool HaveReadyThreads(); | ||||
| 
 | ||||
|     /// Reschedules to the next available thread (call after current thread is suspended)
 | ||||
|     void Reschedule(); | ||||
| 
 | ||||
|     /// Gets the current running thread
 | ||||
|     Thread* GetCurrentThread() const; | ||||
| 
 | ||||
|     /// Adds a new thread to the scheduler
 | ||||
|     void AddThread(SharedPtr<Thread> thread, u32 priority); | ||||
| 
 | ||||
|     /// Removes a thread from the scheduler
 | ||||
|     void RemoveThread(Thread* thread); | ||||
| 
 | ||||
|     /// Schedules a thread that has become "ready"
 | ||||
|     void ScheduleThread(Thread* thread, u32 priority); | ||||
| 
 | ||||
|     /// Unschedules a thread that was already scheduled
 | ||||
|     void UnscheduleThread(Thread* thread, u32 priority); | ||||
| 
 | ||||
|     /// Sets the priority of a thread in the scheduler
 | ||||
|     void SetThreadPriority(Thread* thread, u32 priority); | ||||
| 
 | ||||
|     /// Returns a list of all threads managed by the scheduler
 | ||||
|     const std::vector<SharedPtr<Thread>>& GetThreadList() const { | ||||
|         return thread_list; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     /**
 | ||||
|      * Pops and returns the next thread from the thread queue | ||||
|      * @return A pointer to the next ready thread | ||||
|      */ | ||||
|     Thread* PopNextReadyThread(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Switches the CPU's active thread context to that of the specified thread | ||||
|      * @param new_thread The thread to switch to | ||||
|      */ | ||||
|     void SwitchContext(Thread* new_thread); | ||||
| 
 | ||||
|     /// Lists all thread ids that aren't deleted/etc.
 | ||||
|     std::vector<SharedPtr<Thread>> thread_list; | ||||
| 
 | ||||
|     /// Lists only ready thread ids.
 | ||||
|     Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST + 1> ready_queue; | ||||
| 
 | ||||
|     SharedPtr<Thread> current_thread = nullptr; | ||||
| 
 | ||||
|     ARM_Interface* cpu_core; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Kernel
 | ||||
| @ -483,7 +483,7 @@ static void ExitProcess() { | ||||
|     g_current_process->status = ProcessStatus::Exited; | ||||
| 
 | ||||
|     // Stop all the process threads that are currently waiting for objects.
 | ||||
|     auto& thread_list = GetThreadList(); | ||||
|     auto& thread_list = Core::System::GetInstance().Scheduler().GetThreadList(); | ||||
|     for (auto& thread : thread_list) { | ||||
|         if (thread->owner_process != g_current_process) | ||||
|             continue; | ||||
| @ -585,7 +585,7 @@ static void SleepThread(s64 nanoseconds) { | ||||
| 
 | ||||
|     // Don't attempt to yield execution if there are no available threads to run,
 | ||||
|     // this way we avoid a useless reschedule to the idle thread.
 | ||||
|     if (nanoseconds == 0 && !HaveReadyThreads()) | ||||
|     if (nanoseconds == 0 && !Core::System::GetInstance().Scheduler().HaveReadyThreads()) | ||||
|         return; | ||||
| 
 | ||||
|     // Sleep current thread and check for next thread to schedule
 | ||||
|  | ||||
| @ -41,14 +41,6 @@ void Thread::Acquire(Thread* thread) { | ||||
| //               us to simply use a pool index or similar.
 | ||||
| static Kernel::HandleTable wakeup_callback_handle_table; | ||||
| 
 | ||||
| // Lists all thread ids that aren't deleted/etc.
 | ||||
| static std::vector<SharedPtr<Thread>> thread_list; | ||||
| 
 | ||||
| // Lists only ready thread ids.
 | ||||
| static Common::ThreadQueueList<Thread*, THREADPRIO_LOWEST + 1> ready_queue; | ||||
| 
 | ||||
| static SharedPtr<Thread> current_thread; | ||||
| 
 | ||||
| // The first available thread id at startup
 | ||||
| static u32 next_thread_id; | ||||
| 
 | ||||
| @ -63,10 +55,6 @@ inline static u32 const NewThreadId() { | ||||
| Thread::Thread() {} | ||||
| Thread::~Thread() {} | ||||
| 
 | ||||
| Thread* GetCurrentThread() { | ||||
|     return current_thread.get(); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Check if the specified thread is waiting on the specified address to be arbitrated | ||||
|  * @param thread The thread to test | ||||
| @ -86,7 +74,7 @@ void Thread::Stop() { | ||||
|     // Clean up thread from ready queue
 | ||||
|     // This is only needed when the thread is termintated forcefully (SVC TerminateProcess)
 | ||||
|     if (status == THREADSTATUS_READY) { | ||||
|         ready_queue.remove(current_priority, this); | ||||
|         Core::System::GetInstance().Scheduler().UnscheduleThread(this, current_priority); | ||||
|     } | ||||
| 
 | ||||
|     status = THREADSTATUS_DEAD; | ||||
| @ -109,112 +97,6 @@ void Thread::Stop() { | ||||
|     Kernel::g_current_process->tls_slots[tls_page].reset(tls_slot); | ||||
| } | ||||
| 
 | ||||
| Thread* ArbitrateHighestPriorityThread(u32 address) { | ||||
|     Thread* highest_priority_thread = nullptr; | ||||
|     u32 priority = THREADPRIO_LOWEST; | ||||
| 
 | ||||
|     // Iterate through threads, find highest priority thread that is waiting to be arbitrated...
 | ||||
|     for (auto& thread : thread_list) { | ||||
|         if (!CheckWait_AddressArbiter(thread.get(), address)) | ||||
|             continue; | ||||
| 
 | ||||
|         if (thread == nullptr) | ||||
|             continue; | ||||
| 
 | ||||
|         if (thread->current_priority <= priority) { | ||||
|             highest_priority_thread = thread.get(); | ||||
|             priority = thread->current_priority; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // If a thread was arbitrated, resume it
 | ||||
|     if (nullptr != highest_priority_thread) { | ||||
|         highest_priority_thread->ResumeFromWait(); | ||||
|     } | ||||
| 
 | ||||
|     return highest_priority_thread; | ||||
| } | ||||
| 
 | ||||
| void ArbitrateAllThreads(u32 address) { | ||||
|     // Resume all threads found to be waiting on the address
 | ||||
|     for (auto& thread : thread_list) { | ||||
|         if (CheckWait_AddressArbiter(thread.get(), address)) | ||||
|             thread->ResumeFromWait(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Switches the CPU's active thread context to that of the specified thread | ||||
|  * @param new_thread The thread to switch to | ||||
|  */ | ||||
| static void SwitchContext(Thread* new_thread) { | ||||
|     Thread* previous_thread = GetCurrentThread(); | ||||
| 
 | ||||
|     // Save context for previous thread
 | ||||
|     if (previous_thread) { | ||||
|         previous_thread->last_running_ticks = CoreTiming::GetTicks(); | ||||
|         Core::CPU().SaveContext(previous_thread->context); | ||||
| 
 | ||||
|         if (previous_thread->status == THREADSTATUS_RUNNING) { | ||||
|             // This is only the case when a reschedule is triggered without the current thread
 | ||||
|             // yielding execution (i.e. an event triggered, system core time-sliced, etc)
 | ||||
|             ready_queue.push_front(previous_thread->current_priority, previous_thread); | ||||
|             previous_thread->status = THREADSTATUS_READY; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Load context of new thread
 | ||||
|     if (new_thread) { | ||||
|         ASSERT_MSG(new_thread->status == THREADSTATUS_READY, | ||||
|                    "Thread must be ready to become running."); | ||||
| 
 | ||||
|         // Cancel any outstanding wakeup events for this thread
 | ||||
|         CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle); | ||||
| 
 | ||||
|         auto previous_process = Kernel::g_current_process; | ||||
| 
 | ||||
|         current_thread = new_thread; | ||||
| 
 | ||||
|         ready_queue.remove(new_thread->current_priority, new_thread); | ||||
|         new_thread->status = THREADSTATUS_RUNNING; | ||||
| 
 | ||||
|         if (previous_process != current_thread->owner_process) { | ||||
|             Kernel::g_current_process = current_thread->owner_process; | ||||
|             SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table); | ||||
|         } | ||||
| 
 | ||||
|         Core::CPU().LoadContext(new_thread->context); | ||||
|         Core::CPU().SetTlsAddress(new_thread->GetTLSAddress()); | ||||
|     } else { | ||||
|         current_thread = nullptr; | ||||
|         // Note: We do not reset the current process and current page table when idling because
 | ||||
|         // technically we haven't changed processes, our threads are just paused.
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Pops and returns the next thread from the thread queue | ||||
|  * @return A pointer to the next ready thread | ||||
|  */ | ||||
| static Thread* PopNextReadyThread() { | ||||
|     Thread* next; | ||||
|     Thread* thread = GetCurrentThread(); | ||||
| 
 | ||||
|     if (thread && thread->status == THREADSTATUS_RUNNING) { | ||||
|         // We have to do better than the current thread.
 | ||||
|         // This call returns null when that's not possible.
 | ||||
|         next = ready_queue.pop_first_better(thread->current_priority); | ||||
|         if (!next) { | ||||
|             // Otherwise just keep going with the current thread
 | ||||
|             next = thread; | ||||
|         } | ||||
|     } else { | ||||
|         next = ready_queue.pop_first(); | ||||
|     } | ||||
| 
 | ||||
|     return next; | ||||
| } | ||||
| 
 | ||||
| void WaitCurrentThread_Sleep() { | ||||
|     Thread* thread = GetCurrentThread(); | ||||
|     thread->status = THREADSTATUS_WAIT_SLEEP; | ||||
| @ -229,8 +111,7 @@ void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) { | ||||
| void ExitCurrentThread() { | ||||
|     Thread* thread = GetCurrentThread(); | ||||
|     thread->Stop(); | ||||
|     thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread), | ||||
|                       thread_list.end()); | ||||
|     Core::System::GetInstance().Scheduler().RemoveThread(thread); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
| @ -308,31 +189,11 @@ void Thread::ResumeFromWait() { | ||||
| 
 | ||||
|     wakeup_callback = nullptr; | ||||
| 
 | ||||
|     ready_queue.push_back(current_priority, this); | ||||
|     status = THREADSTATUS_READY; | ||||
|     Core::System::GetInstance().Scheduler().ScheduleThread(this, current_priority); | ||||
|     Core::System::GetInstance().PrepareReschedule(); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Prints the thread queue for debugging purposes | ||||
|  */ | ||||
| static void DebugThreadQueue() { | ||||
|     Thread* thread = GetCurrentThread(); | ||||
|     if (!thread) { | ||||
|         LOG_DEBUG(Kernel, "Current: NO CURRENT THREAD"); | ||||
|     } else { | ||||
|         LOG_DEBUG(Kernel, "0x%02X %u (current)", thread->current_priority, | ||||
|                   GetCurrentThread()->GetObjectId()); | ||||
|     } | ||||
| 
 | ||||
|     for (auto& t : thread_list) { | ||||
|         u32 priority = ready_queue.contains(t.get()); | ||||
|         if (priority != -1) { | ||||
|             LOG_DEBUG(Kernel, "0x%02X %u", priority, t->GetObjectId()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Finds a free location for the TLS section of a thread. | ||||
|  * @param tls_slots The TLS page array of the thread's owner process. | ||||
| @ -400,8 +261,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, | ||||
| 
 | ||||
|     SharedPtr<Thread> thread(new Thread); | ||||
| 
 | ||||
|     thread_list.push_back(thread); | ||||
|     ready_queue.prepare(priority); | ||||
|     Core::System::GetInstance().Scheduler().AddThread(thread, priority); | ||||
| 
 | ||||
|     thread->thread_id = NewThreadId(); | ||||
|     thread->status = THREADSTATUS_DORMANT; | ||||
| @ -472,12 +332,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, | ||||
| void Thread::SetPriority(u32 priority) { | ||||
|     ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST, | ||||
|                "Invalid priority value."); | ||||
|     // If thread was ready, adjust queues
 | ||||
|     if (status == THREADSTATUS_READY) | ||||
|         ready_queue.move(this, current_priority, priority); | ||||
|     else | ||||
|         ready_queue.prepare(priority); | ||||
| 
 | ||||
|     Core::System::GetInstance().Scheduler().SetThreadPriority(this, priority); | ||||
|     nominal_priority = current_priority = priority; | ||||
| } | ||||
| 
 | ||||
| @ -491,11 +346,7 @@ void Thread::UpdatePriority() { | ||||
| } | ||||
| 
 | ||||
| void Thread::BoostPriority(u32 priority) { | ||||
|     // If thread was ready, adjust queues
 | ||||
|     if (status == THREADSTATUS_READY) | ||||
|         ready_queue.move(this, current_priority, priority); | ||||
|     else | ||||
|         ready_queue.prepare(priority); | ||||
|     Core::System::GetInstance().Scheduler().SetThreadPriority(this, priority); | ||||
|     current_priority = priority; | ||||
| } | ||||
| 
 | ||||
| @ -521,25 +372,6 @@ SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority, | ||||
|     return thread; | ||||
| } | ||||
| 
 | ||||
| bool HaveReadyThreads() { | ||||
|     return ready_queue.get_first() != nullptr; | ||||
| } | ||||
| 
 | ||||
| void Reschedule() { | ||||
|     Thread* cur = GetCurrentThread(); | ||||
|     Thread* next = PopNextReadyThread(); | ||||
| 
 | ||||
|     if (cur && next) { | ||||
|         LOG_TRACE(Kernel, "context switch %u -> %u", cur->GetObjectId(), next->GetObjectId()); | ||||
|     } else if (cur) { | ||||
|         LOG_TRACE(Kernel, "context switch %u -> idle", cur->GetObjectId()); | ||||
|     } else if (next) { | ||||
|         LOG_TRACE(Kernel, "context switch idle -> %u", next->GetObjectId()); | ||||
|     } | ||||
| 
 | ||||
|     SwitchContext(next); | ||||
| } | ||||
| 
 | ||||
| void Thread::SetWaitSynchronizationResult(ResultCode result) { | ||||
|     context.cpu_registers[0] = result.raw; | ||||
| } | ||||
| @ -562,25 +394,18 @@ VAddr Thread::GetCommandBufferAddress() const { | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
| /**
 | ||||
|  * Gets the current thread | ||||
|  */ | ||||
| Thread* GetCurrentThread() { | ||||
|     return Core::System::GetInstance().Scheduler().GetCurrentThread(); | ||||
| } | ||||
| 
 | ||||
| void ThreadingInit() { | ||||
|     ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback); | ||||
| 
 | ||||
|     current_thread = nullptr; | ||||
|     next_thread_id = 1; | ||||
| } | ||||
| 
 | ||||
| void ThreadingShutdown() { | ||||
|     current_thread = nullptr; | ||||
| 
 | ||||
|     for (auto& t : thread_list) { | ||||
|         t->Stop(); | ||||
|     } | ||||
|     thread_list.clear(); | ||||
|     ready_queue.clear(); | ||||
| } | ||||
| 
 | ||||
| const std::vector<SharedPtr<Thread>>& GetThreadList() { | ||||
|     return thread_list; | ||||
| } | ||||
| void ThreadingShutdown() {} | ||||
| 
 | ||||
| } // namespace Kernel
 | ||||
|  | ||||
| @ -249,28 +249,6 @@ private: | ||||
| SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority, | ||||
|                                   SharedPtr<Process> owner_process); | ||||
| 
 | ||||
| /**
 | ||||
|  * Returns whether there are any threads that are ready to run. | ||||
|  */ | ||||
| bool HaveReadyThreads(); | ||||
| 
 | ||||
| /**
 | ||||
|  * Reschedules to the next available thread (call after current thread is suspended) | ||||
|  */ | ||||
| void Reschedule(); | ||||
| 
 | ||||
| /**
 | ||||
|  * Arbitrate the highest priority thread that is waiting | ||||
|  * @param address The address for which waiting threads should be arbitrated | ||||
|  */ | ||||
| Thread* ArbitrateHighestPriorityThread(VAddr address); | ||||
| 
 | ||||
| /**
 | ||||
|  * Arbitrate all threads currently waiting. | ||||
|  * @param address The address for which waiting threads should be arbitrated | ||||
|  */ | ||||
| void ArbitrateAllThreads(VAddr address); | ||||
| 
 | ||||
| /**
 | ||||
|  * Gets the current thread | ||||
|  */ | ||||
| @ -302,9 +280,4 @@ void ThreadingInit(); | ||||
|  */ | ||||
| void ThreadingShutdown(); | ||||
| 
 | ||||
| /**
 | ||||
|  * Get a const reference to the thread list for debug use | ||||
|  */ | ||||
| const std::vector<SharedPtr<Thread>>& GetThreadList(); | ||||
| 
 | ||||
| } // namespace Kernel
 | ||||
|  | ||||
| @ -5,6 +5,7 @@ | ||||
| #include "yuzu/debugger/wait_tree.h" | ||||
| #include "yuzu/util/util.h" | ||||
| 
 | ||||
| #include "core/core.h" | ||||
| #include "core/hle/kernel/condition_variable.h" | ||||
| #include "core/hle/kernel/event.h" | ||||
| #include "core/hle/kernel/mutex.h" | ||||
| @ -50,7 +51,7 @@ std::size_t WaitTreeItem::Row() const { | ||||
| } | ||||
| 
 | ||||
| std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList() { | ||||
|     const auto& threads = Kernel::GetThreadList(); | ||||
|     const auto& threads = Core::System::GetInstance().Scheduler().GetThreadList(); | ||||
|     std::vector<std::unique_ptr<WaitTreeThread>> item_list; | ||||
|     item_list.reserve(threads.size()); | ||||
|     for (std::size_t i = 0; i < threads.size(); ++i) { | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 bunnei
						bunnei