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/config_mem.h | ||||||
|     hle/ipc.h |     hle/ipc.h | ||||||
|     hle/ipc_helpers.h |     hle/ipc_helpers.h | ||||||
|     hle/kernel/address_arbiter.cpp |  | ||||||
|     hle/kernel/address_arbiter.h |  | ||||||
|     hle/kernel/client_port.cpp |     hle/kernel/client_port.cpp | ||||||
|     hle/kernel/client_port.h |     hle/kernel/client_port.h | ||||||
|     hle/kernel/client_session.cpp |     hle/kernel/client_session.cpp | ||||||
| @ -55,6 +53,8 @@ add_library(core STATIC | |||||||
|     hle/kernel/process.h |     hle/kernel/process.h | ||||||
|     hle/kernel/resource_limit.cpp |     hle/kernel/resource_limit.cpp | ||||||
|     hle/kernel/resource_limit.h |     hle/kernel/resource_limit.h | ||||||
|  |     hle/kernel/scheduler.cpp | ||||||
|  |     hle/kernel/scheduler.h | ||||||
|     hle/kernel/server_port.cpp |     hle/kernel/server_port.cpp | ||||||
|     hle/kernel/server_port.h |     hle/kernel/server_port.h | ||||||
|     hle/kernel/server_session.cpp |     hle/kernel/server_session.cpp | ||||||
|  | |||||||
| @ -133,7 +133,7 @@ void System::Reschedule() { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     reschedule_pending = false; |     reschedule_pending = false; | ||||||
|     Kernel::Reschedule(); |     Core::System::GetInstance().Scheduler().Reschedule(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { | 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) { |     switch (Settings::values.cpu_core) { | ||||||
|     case Settings::CpuCore::Unicorn: |     case Settings::CpuCore::Unicorn: | ||||||
|         cpu_core = std::make_unique<ARM_Unicorn>(); |         cpu_core = std::make_shared<ARM_Unicorn>(); | ||||||
|         break; |         break; | ||||||
|     case Settings::CpuCore::Dynarmic: |     case Settings::CpuCore::Dynarmic: | ||||||
|     default: |     default: | ||||||
| #ifdef ARCHITECTURE_x86_64 | #ifdef ARCHITECTURE_x86_64 | ||||||
|         cpu_core = std::make_unique<ARM_Dynarmic>(); |         cpu_core = std::make_shared<ARM_Dynarmic>(); | ||||||
| #else | #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"); |         LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); | ||||||
| #endif | #endif | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     scheduler = std::make_unique<Kernel::Scheduler>(cpu_core.get()); | ||||||
|     gpu_core = std::make_unique<Tegra::GPU>(); |     gpu_core = std::make_unique<Tegra::GPU>(); | ||||||
| 
 | 
 | ||||||
|     telemetry_session = std::make_unique<Core::TelemetrySession>(); |     telemetry_session = std::make_unique<Core::TelemetrySession>(); | ||||||
|  | |||||||
| @ -7,6 +7,7 @@ | |||||||
| #include <memory> | #include <memory> | ||||||
| #include <string> | #include <string> | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
|  | #include "core/hle/kernel/scheduler.h" | ||||||
| #include "core/loader/loader.h" | #include "core/loader/loader.h" | ||||||
| #include "core/memory.h" | #include "core/memory.h" | ||||||
| #include "core/perf_stats.h" | #include "core/perf_stats.h" | ||||||
| @ -107,6 +108,10 @@ public: | |||||||
|         return *gpu_core; |         return *gpu_core; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     Kernel::Scheduler& Scheduler() { | ||||||
|  |         return *scheduler; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     PerfStats perf_stats; |     PerfStats perf_stats; | ||||||
|     FrameLimiter frame_limiter; |     FrameLimiter frame_limiter; | ||||||
| 
 | 
 | ||||||
| @ -140,9 +145,8 @@ private: | |||||||
|     /// AppLoader used to load the current executing application
 |     /// AppLoader used to load the current executing application
 | ||||||
|     std::unique_ptr<Loader::AppLoader> app_loader; |     std::unique_ptr<Loader::AppLoader> app_loader; | ||||||
| 
 | 
 | ||||||
|     ///< ARM11 CPU core
 |     std::shared_ptr<ARM_Interface> cpu_core; | ||||||
|     std::unique_ptr<ARM_Interface> cpu_core; |     std::unique_ptr<Kernel::Scheduler> scheduler; | ||||||
| 
 |  | ||||||
|     std::unique_ptr<Tegra::GPU> gpu_core; |     std::unique_ptr<Tegra::GPU> gpu_core; | ||||||
| 
 | 
 | ||||||
|     /// When true, signals that a reschedule should happen
 |     /// 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; |     g_current_process->status = ProcessStatus::Exited; | ||||||
| 
 | 
 | ||||||
|     // Stop all the process threads that are currently waiting for objects.
 |     // 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) { |     for (auto& thread : thread_list) { | ||||||
|         if (thread->owner_process != g_current_process) |         if (thread->owner_process != g_current_process) | ||||||
|             continue; |             continue; | ||||||
| @ -585,7 +585,7 @@ static void SleepThread(s64 nanoseconds) { | |||||||
| 
 | 
 | ||||||
|     // Don't attempt to yield execution if there are no available threads to run,
 |     // 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.
 |     // this way we avoid a useless reschedule to the idle thread.
 | ||||||
|     if (nanoseconds == 0 && !HaveReadyThreads()) |     if (nanoseconds == 0 && !Core::System::GetInstance().Scheduler().HaveReadyThreads()) | ||||||
|         return; |         return; | ||||||
| 
 | 
 | ||||||
|     // Sleep current thread and check for next thread to schedule
 |     // 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.
 | //               us to simply use a pool index or similar.
 | ||||||
| static Kernel::HandleTable wakeup_callback_handle_table; | 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
 | // The first available thread id at startup
 | ||||||
| static u32 next_thread_id; | static u32 next_thread_id; | ||||||
| 
 | 
 | ||||||
| @ -63,10 +55,6 @@ inline static u32 const NewThreadId() { | |||||||
| Thread::Thread() {} | Thread::Thread() {} | ||||||
| Thread::~Thread() {} | Thread::~Thread() {} | ||||||
| 
 | 
 | ||||||
| Thread* GetCurrentThread() { |  | ||||||
|     return current_thread.get(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /**
 | /**
 | ||||||
|  * Check if the specified thread is waiting on the specified address to be arbitrated |  * Check if the specified thread is waiting on the specified address to be arbitrated | ||||||
|  * @param thread The thread to test |  * @param thread The thread to test | ||||||
| @ -86,7 +74,7 @@ void Thread::Stop() { | |||||||
|     // Clean up thread from ready queue
 |     // Clean up thread from ready queue
 | ||||||
|     // This is only needed when the thread is termintated forcefully (SVC TerminateProcess)
 |     // This is only needed when the thread is termintated forcefully (SVC TerminateProcess)
 | ||||||
|     if (status == THREADSTATUS_READY) { |     if (status == THREADSTATUS_READY) { | ||||||
|         ready_queue.remove(current_priority, this); |         Core::System::GetInstance().Scheduler().UnscheduleThread(this, current_priority); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     status = THREADSTATUS_DEAD; |     status = THREADSTATUS_DEAD; | ||||||
| @ -109,112 +97,6 @@ void Thread::Stop() { | |||||||
|     Kernel::g_current_process->tls_slots[tls_page].reset(tls_slot); |     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() { | void WaitCurrentThread_Sleep() { | ||||||
|     Thread* thread = GetCurrentThread(); |     Thread* thread = GetCurrentThread(); | ||||||
|     thread->status = THREADSTATUS_WAIT_SLEEP; |     thread->status = THREADSTATUS_WAIT_SLEEP; | ||||||
| @ -229,8 +111,7 @@ void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) { | |||||||
| void ExitCurrentThread() { | void ExitCurrentThread() { | ||||||
|     Thread* thread = GetCurrentThread(); |     Thread* thread = GetCurrentThread(); | ||||||
|     thread->Stop(); |     thread->Stop(); | ||||||
|     thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread), |     Core::System::GetInstance().Scheduler().RemoveThread(thread); | ||||||
|                       thread_list.end()); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
| @ -308,31 +189,11 @@ void Thread::ResumeFromWait() { | |||||||
| 
 | 
 | ||||||
|     wakeup_callback = nullptr; |     wakeup_callback = nullptr; | ||||||
| 
 | 
 | ||||||
|     ready_queue.push_back(current_priority, this); |  | ||||||
|     status = THREADSTATUS_READY; |     status = THREADSTATUS_READY; | ||||||
|  |     Core::System::GetInstance().Scheduler().ScheduleThread(this, current_priority); | ||||||
|     Core::System::GetInstance().PrepareReschedule(); |     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. |  * Finds a free location for the TLS section of a thread. | ||||||
|  * @param tls_slots The TLS page array of the thread's owner process. |  * @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); |     SharedPtr<Thread> thread(new Thread); | ||||||
| 
 | 
 | ||||||
|     thread_list.push_back(thread); |     Core::System::GetInstance().Scheduler().AddThread(thread, priority); | ||||||
|     ready_queue.prepare(priority); |  | ||||||
| 
 | 
 | ||||||
|     thread->thread_id = NewThreadId(); |     thread->thread_id = NewThreadId(); | ||||||
|     thread->status = THREADSTATUS_DORMANT; |     thread->status = THREADSTATUS_DORMANT; | ||||||
| @ -472,12 +332,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, | |||||||
| void Thread::SetPriority(u32 priority) { | void Thread::SetPriority(u32 priority) { | ||||||
|     ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST, |     ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST, | ||||||
|                "Invalid priority value."); |                "Invalid priority value."); | ||||||
|     // If thread was ready, adjust queues
 |     Core::System::GetInstance().Scheduler().SetThreadPriority(this, priority); | ||||||
|     if (status == THREADSTATUS_READY) |  | ||||||
|         ready_queue.move(this, current_priority, priority); |  | ||||||
|     else |  | ||||||
|         ready_queue.prepare(priority); |  | ||||||
| 
 |  | ||||||
|     nominal_priority = current_priority = priority; |     nominal_priority = current_priority = priority; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -491,11 +346,7 @@ void Thread::UpdatePriority() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Thread::BoostPriority(u32 priority) { | void Thread::BoostPriority(u32 priority) { | ||||||
|     // If thread was ready, adjust queues
 |     Core::System::GetInstance().Scheduler().SetThreadPriority(this, priority); | ||||||
|     if (status == THREADSTATUS_READY) |  | ||||||
|         ready_queue.move(this, current_priority, priority); |  | ||||||
|     else |  | ||||||
|         ready_queue.prepare(priority); |  | ||||||
|     current_priority = priority; |     current_priority = priority; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -521,25 +372,6 @@ SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority, | |||||||
|     return thread; |     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) { | void Thread::SetWaitSynchronizationResult(ResultCode result) { | ||||||
|     context.cpu_registers[0] = result.raw; |     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() { | void ThreadingInit() { | ||||||
|     ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback); |     ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback); | ||||||
| 
 |  | ||||||
|     current_thread = nullptr; |  | ||||||
|     next_thread_id = 1; |     next_thread_id = 1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ThreadingShutdown() { | 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; |  | ||||||
| } |  | ||||||
| 
 | 
 | ||||||
| } // namespace Kernel
 | } // namespace Kernel
 | ||||||
|  | |||||||
| @ -249,28 +249,6 @@ private: | |||||||
| SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority, | SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority, | ||||||
|                                   SharedPtr<Process> owner_process); |                                   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 |  * Gets the current thread | ||||||
|  */ |  */ | ||||||
| @ -302,9 +280,4 @@ void ThreadingInit(); | |||||||
|  */ |  */ | ||||||
| void ThreadingShutdown(); | void ThreadingShutdown(); | ||||||
| 
 | 
 | ||||||
| /**
 |  | ||||||
|  * Get a const reference to the thread list for debug use |  | ||||||
|  */ |  | ||||||
| const std::vector<SharedPtr<Thread>>& GetThreadList(); |  | ||||||
| 
 |  | ||||||
| } // namespace Kernel
 | } // namespace Kernel
 | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ | |||||||
| #include "yuzu/debugger/wait_tree.h" | #include "yuzu/debugger/wait_tree.h" | ||||||
| #include "yuzu/util/util.h" | #include "yuzu/util/util.h" | ||||||
| 
 | 
 | ||||||
|  | #include "core/core.h" | ||||||
| #include "core/hle/kernel/condition_variable.h" | #include "core/hle/kernel/condition_variable.h" | ||||||
| #include "core/hle/kernel/event.h" | #include "core/hle/kernel/event.h" | ||||||
| #include "core/hle/kernel/mutex.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() { | 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; |     std::vector<std::unique_ptr<WaitTreeThread>> item_list; | ||||||
|     item_list.reserve(threads.size()); |     item_list.reserve(threads.size()); | ||||||
|     for (std::size_t i = 0; i < threads.size(); ++i) { |     for (std::size_t i = 0; i < threads.size(); ++i) { | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 bunnei
						bunnei