mirror of
				https://git.suyu.dev/suyu/suyu.git
				synced 2025-10-25 11:56:42 +08:00 
			
		
		
		
	Merge pull request #3416 from FernandoS27/schedule
Kernel: Refactors and Implement a TimeManager and SchedulerLocks
This commit is contained in:
		
						commit
						3ef5f2017d
					
				| @ -187,6 +187,8 @@ add_library(core STATIC | |||||||
|     hle/kernel/synchronization.h |     hle/kernel/synchronization.h | ||||||
|     hle/kernel/thread.cpp |     hle/kernel/thread.cpp | ||||||
|     hle/kernel/thread.h |     hle/kernel/thread.h | ||||||
|  |     hle/kernel/time_manager.cpp | ||||||
|  |     hle/kernel/time_manager.h | ||||||
|     hle/kernel/transfer_memory.cpp |     hle/kernel/transfer_memory.cpp | ||||||
|     hle/kernel/transfer_memory.h |     hle/kernel/transfer_memory.h | ||||||
|     hle/kernel/vm_manager.cpp |     hle/kernel/vm_manager.cpp | ||||||
|  | |||||||
| @ -707,4 +707,12 @@ const Service::SM::ServiceManager& System::ServiceManager() const { | |||||||
|     return *impl->service_manager; |     return *impl->service_manager; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void System::RegisterCoreThread(std::size_t id) { | ||||||
|  |     impl->kernel.RegisterCoreThread(id); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void System::RegisterHostThread() { | ||||||
|  |     impl->kernel.RegisterHostThread(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } // namespace Core
 | } // namespace Core
 | ||||||
|  | |||||||
| @ -360,6 +360,12 @@ public: | |||||||
| 
 | 
 | ||||||
|     const CurrentBuildProcessID& GetCurrentProcessBuildID() const; |     const CurrentBuildProcessID& GetCurrentProcessBuildID() const; | ||||||
| 
 | 
 | ||||||
|  |     /// Register a host thread as an emulated CPU Core.
 | ||||||
|  |     void RegisterCoreThread(std::size_t id); | ||||||
|  | 
 | ||||||
|  |     /// Register a host thread as an auxiliary thread.
 | ||||||
|  |     void RegisterHostThread(); | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|     System(); |     System(); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -20,6 +20,8 @@ constexpr u32 NUM_CPU_CORES = 4;            // Number of CPU Cores | |||||||
| 
 | 
 | ||||||
| } // namespace Hardware
 | } // namespace Hardware
 | ||||||
| 
 | 
 | ||||||
|  | constexpr u32 INVALID_HOST_THREAD_ID = 0xFFFFFFFF; | ||||||
|  | 
 | ||||||
| struct EmuThreadHandle { | struct EmuThreadHandle { | ||||||
|     u32 host_handle; |     u32 host_handle; | ||||||
|     u32 guest_handle; |     u32 guest_handle; | ||||||
|  | |||||||
| @ -3,9 +3,12 @@ | |||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #include <atomic> | #include <atomic> | ||||||
|  | #include <bitset> | ||||||
| #include <functional> | #include <functional> | ||||||
| #include <memory> | #include <memory> | ||||||
| #include <mutex> | #include <mutex> | ||||||
|  | #include <thread> | ||||||
|  | #include <unordered_map> | ||||||
| #include <utility> | #include <utility> | ||||||
| 
 | 
 | ||||||
| #include "common/assert.h" | #include "common/assert.h" | ||||||
| @ -15,6 +18,7 @@ | |||||||
| #include "core/core.h" | #include "core/core.h" | ||||||
| #include "core/core_timing.h" | #include "core/core_timing.h" | ||||||
| #include "core/core_timing_util.h" | #include "core/core_timing_util.h" | ||||||
|  | #include "core/hardware_properties.h" | ||||||
| #include "core/hle/kernel/client_port.h" | #include "core/hle/kernel/client_port.h" | ||||||
| #include "core/hle/kernel/errors.h" | #include "core/hle/kernel/errors.h" | ||||||
| #include "core/hle/kernel/handle_table.h" | #include "core/hle/kernel/handle_table.h" | ||||||
| @ -25,6 +29,7 @@ | |||||||
| #include "core/hle/kernel/scheduler.h" | #include "core/hle/kernel/scheduler.h" | ||||||
| #include "core/hle/kernel/synchronization.h" | #include "core/hle/kernel/synchronization.h" | ||||||
| #include "core/hle/kernel/thread.h" | #include "core/hle/kernel/thread.h" | ||||||
|  | #include "core/hle/kernel/time_manager.h" | ||||||
| #include "core/hle/lock.h" | #include "core/hle/lock.h" | ||||||
| #include "core/hle/result.h" | #include "core/hle/result.h" | ||||||
| #include "core/memory.h" | #include "core/memory.h" | ||||||
| @ -44,7 +49,7 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_ | |||||||
|     std::lock_guard lock{HLE::g_hle_lock}; |     std::lock_guard lock{HLE::g_hle_lock}; | ||||||
| 
 | 
 | ||||||
|     std::shared_ptr<Thread> thread = |     std::shared_ptr<Thread> thread = | ||||||
|         system.Kernel().RetrieveThreadFromWakeupCallbackHandleTable(proper_handle); |         system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle); | ||||||
|     if (thread == nullptr) { |     if (thread == nullptr) { | ||||||
|         LOG_CRITICAL(Kernel, "Callback fired for invalid thread {:08X}", proper_handle); |         LOG_CRITICAL(Kernel, "Callback fired for invalid thread {:08X}", proper_handle); | ||||||
|         return; |         return; | ||||||
| @ -97,8 +102,8 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_ | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| struct KernelCore::Impl { | struct KernelCore::Impl { | ||||||
|     explicit Impl(Core::System& system) |     explicit Impl(Core::System& system, KernelCore& kernel) | ||||||
|         : system{system}, global_scheduler{system}, synchronization{system} {} |         : system{system}, global_scheduler{kernel}, synchronization{system}, time_manager{system} {} | ||||||
| 
 | 
 | ||||||
|     void Initialize(KernelCore& kernel) { |     void Initialize(KernelCore& kernel) { | ||||||
|         Shutdown(); |         Shutdown(); | ||||||
| @ -120,7 +125,7 @@ struct KernelCore::Impl { | |||||||
| 
 | 
 | ||||||
|         system_resource_limit = nullptr; |         system_resource_limit = nullptr; | ||||||
| 
 | 
 | ||||||
|         thread_wakeup_callback_handle_table.Clear(); |         global_handle_table.Clear(); | ||||||
|         thread_wakeup_event_type = nullptr; |         thread_wakeup_event_type = nullptr; | ||||||
|         preemption_event = nullptr; |         preemption_event = nullptr; | ||||||
| 
 | 
 | ||||||
| @ -138,8 +143,8 @@ struct KernelCore::Impl { | |||||||
| 
 | 
 | ||||||
|     void InitializePhysicalCores() { |     void InitializePhysicalCores() { | ||||||
|         exclusive_monitor = |         exclusive_monitor = | ||||||
|             Core::MakeExclusiveMonitor(system.Memory(), global_scheduler.CpuCoresCount()); |             Core::MakeExclusiveMonitor(system.Memory(), Core::Hardware::NUM_CPU_CORES); | ||||||
|         for (std::size_t i = 0; i < global_scheduler.CpuCoresCount(); i++) { |         for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { | ||||||
|             cores.emplace_back(system, i, *exclusive_monitor); |             cores.emplace_back(system, i, *exclusive_monitor); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @ -184,6 +189,50 @@ struct KernelCore::Impl { | |||||||
|         system.Memory().SetCurrentPageTable(*process); |         system.Memory().SetCurrentPageTable(*process); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     void RegisterCoreThread(std::size_t core_id) { | ||||||
|  |         std::unique_lock lock{register_thread_mutex}; | ||||||
|  |         const std::thread::id this_id = std::this_thread::get_id(); | ||||||
|  |         const auto it = host_thread_ids.find(this_id); | ||||||
|  |         ASSERT(core_id < Core::Hardware::NUM_CPU_CORES); | ||||||
|  |         ASSERT(it == host_thread_ids.end()); | ||||||
|  |         ASSERT(!registered_core_threads[core_id]); | ||||||
|  |         host_thread_ids[this_id] = static_cast<u32>(core_id); | ||||||
|  |         registered_core_threads.set(core_id); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void RegisterHostThread() { | ||||||
|  |         std::unique_lock lock{register_thread_mutex}; | ||||||
|  |         const std::thread::id this_id = std::this_thread::get_id(); | ||||||
|  |         const auto it = host_thread_ids.find(this_id); | ||||||
|  |         ASSERT(it == host_thread_ids.end()); | ||||||
|  |         host_thread_ids[this_id] = registered_thread_ids++; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     u32 GetCurrentHostThreadID() const { | ||||||
|  |         const std::thread::id this_id = std::this_thread::get_id(); | ||||||
|  |         const auto it = host_thread_ids.find(this_id); | ||||||
|  |         if (it == host_thread_ids.end()) { | ||||||
|  |             return Core::INVALID_HOST_THREAD_ID; | ||||||
|  |         } | ||||||
|  |         return it->second; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     Core::EmuThreadHandle GetCurrentEmuThreadID() const { | ||||||
|  |         Core::EmuThreadHandle result = Core::EmuThreadHandle::InvalidHandle(); | ||||||
|  |         result.host_handle = GetCurrentHostThreadID(); | ||||||
|  |         if (result.host_handle >= Core::Hardware::NUM_CPU_CORES) { | ||||||
|  |             return result; | ||||||
|  |         } | ||||||
|  |         const Kernel::Scheduler& sched = cores[result.host_handle].Scheduler(); | ||||||
|  |         const Kernel::Thread* current = sched.GetCurrentThread(); | ||||||
|  |         if (current != nullptr) { | ||||||
|  |             result.guest_handle = current->GetGlobalHandle(); | ||||||
|  |         } else { | ||||||
|  |             result.guest_handle = InvalidHandle; | ||||||
|  |         } | ||||||
|  |         return result; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     std::atomic<u32> next_object_id{0}; |     std::atomic<u32> next_object_id{0}; | ||||||
|     std::atomic<u64> next_kernel_process_id{Process::InitialKIPIDMin}; |     std::atomic<u64> next_kernel_process_id{Process::InitialKIPIDMin}; | ||||||
|     std::atomic<u64> next_user_process_id{Process::ProcessIDMin}; |     std::atomic<u64> next_user_process_id{Process::ProcessIDMin}; | ||||||
| @ -194,15 +243,16 @@ struct KernelCore::Impl { | |||||||
|     Process* current_process = nullptr; |     Process* current_process = nullptr; | ||||||
|     Kernel::GlobalScheduler global_scheduler; |     Kernel::GlobalScheduler global_scheduler; | ||||||
|     Kernel::Synchronization synchronization; |     Kernel::Synchronization synchronization; | ||||||
|  |     Kernel::TimeManager time_manager; | ||||||
| 
 | 
 | ||||||
|     std::shared_ptr<ResourceLimit> system_resource_limit; |     std::shared_ptr<ResourceLimit> system_resource_limit; | ||||||
| 
 | 
 | ||||||
|     std::shared_ptr<Core::Timing::EventType> thread_wakeup_event_type; |     std::shared_ptr<Core::Timing::EventType> thread_wakeup_event_type; | ||||||
|     std::shared_ptr<Core::Timing::EventType> preemption_event; |     std::shared_ptr<Core::Timing::EventType> preemption_event; | ||||||
| 
 | 
 | ||||||
|     // TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future,
 |     // This is the kernel's handle table or supervisor handle table which
 | ||||||
|     // allowing us to simply use a pool index or similar.
 |     // stores all the objects in place.
 | ||||||
|     Kernel::HandleTable thread_wakeup_callback_handle_table; |     Kernel::HandleTable global_handle_table; | ||||||
| 
 | 
 | ||||||
|     /// Map of named ports managed by the kernel, which can be retrieved using
 |     /// Map of named ports managed by the kernel, which can be retrieved using
 | ||||||
|     /// the ConnectToPort SVC.
 |     /// the ConnectToPort SVC.
 | ||||||
| @ -211,11 +261,17 @@ struct KernelCore::Impl { | |||||||
|     std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor; |     std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor; | ||||||
|     std::vector<Kernel::PhysicalCore> cores; |     std::vector<Kernel::PhysicalCore> cores; | ||||||
| 
 | 
 | ||||||
|  |     // 0-3 IDs represent core threads, >3 represent others
 | ||||||
|  |     std::unordered_map<std::thread::id, u32> host_thread_ids; | ||||||
|  |     u32 registered_thread_ids{Core::Hardware::NUM_CPU_CORES}; | ||||||
|  |     std::bitset<Core::Hardware::NUM_CPU_CORES> registered_core_threads; | ||||||
|  |     std::mutex register_thread_mutex; | ||||||
|  | 
 | ||||||
|     // System context
 |     // System context
 | ||||||
|     Core::System& system; |     Core::System& system; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| KernelCore::KernelCore(Core::System& system) : impl{std::make_unique<Impl>(system)} {} | KernelCore::KernelCore(Core::System& system) : impl{std::make_unique<Impl>(system, *this)} {} | ||||||
| KernelCore::~KernelCore() { | KernelCore::~KernelCore() { | ||||||
|     Shutdown(); |     Shutdown(); | ||||||
| } | } | ||||||
| @ -232,9 +288,8 @@ std::shared_ptr<ResourceLimit> KernelCore::GetSystemResourceLimit() const { | |||||||
|     return impl->system_resource_limit; |     return impl->system_resource_limit; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::shared_ptr<Thread> KernelCore::RetrieveThreadFromWakeupCallbackHandleTable( | std::shared_ptr<Thread> KernelCore::RetrieveThreadFromGlobalHandleTable(Handle handle) const { | ||||||
|     Handle handle) const { |     return impl->global_handle_table.Get<Thread>(handle); | ||||||
|     return impl->thread_wakeup_callback_handle_table.Get<Thread>(handle); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void KernelCore::AppendNewProcess(std::shared_ptr<Process> process) { | void KernelCore::AppendNewProcess(std::shared_ptr<Process> process) { | ||||||
| @ -265,6 +320,14 @@ const Kernel::GlobalScheduler& KernelCore::GlobalScheduler() const { | |||||||
|     return impl->global_scheduler; |     return impl->global_scheduler; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | Kernel::Scheduler& KernelCore::Scheduler(std::size_t id) { | ||||||
|  |     return impl->cores[id].Scheduler(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const Kernel::Scheduler& KernelCore::Scheduler(std::size_t id) const { | ||||||
|  |     return impl->cores[id].Scheduler(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) { | Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) { | ||||||
|     return impl->cores[id]; |     return impl->cores[id]; | ||||||
| } | } | ||||||
| @ -281,6 +344,14 @@ const Kernel::Synchronization& KernelCore::Synchronization() const { | |||||||
|     return impl->synchronization; |     return impl->synchronization; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | Kernel::TimeManager& KernelCore::TimeManager() { | ||||||
|  |     return impl->time_manager; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const Kernel::TimeManager& KernelCore::TimeManager() const { | ||||||
|  |     return impl->time_manager; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() { | Core::ExclusiveMonitor& KernelCore::GetExclusiveMonitor() { | ||||||
|     return *impl->exclusive_monitor; |     return *impl->exclusive_monitor; | ||||||
| } | } | ||||||
| @ -338,12 +409,28 @@ const std::shared_ptr<Core::Timing::EventType>& KernelCore::ThreadWakeupCallback | |||||||
|     return impl->thread_wakeup_event_type; |     return impl->thread_wakeup_event_type; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Kernel::HandleTable& KernelCore::ThreadWakeupCallbackHandleTable() { | Kernel::HandleTable& KernelCore::GlobalHandleTable() { | ||||||
|     return impl->thread_wakeup_callback_handle_table; |     return impl->global_handle_table; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const Kernel::HandleTable& KernelCore::ThreadWakeupCallbackHandleTable() const { | const Kernel::HandleTable& KernelCore::GlobalHandleTable() const { | ||||||
|     return impl->thread_wakeup_callback_handle_table; |     return impl->global_handle_table; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void KernelCore::RegisterCoreThread(std::size_t core_id) { | ||||||
|  |     impl->RegisterCoreThread(core_id); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void KernelCore::RegisterHostThread() { | ||||||
|  |     impl->RegisterHostThread(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | u32 KernelCore::GetCurrentHostThreadID() const { | ||||||
|  |     return impl->GetCurrentHostThreadID(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Core::EmuThreadHandle KernelCore::GetCurrentEmuThreadID() const { | ||||||
|  |     return impl->GetCurrentEmuThreadID(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| } // namespace Kernel
 | } // namespace Kernel
 | ||||||
|  | |||||||
| @ -11,6 +11,7 @@ | |||||||
| #include "core/hle/kernel/object.h" | #include "core/hle/kernel/object.h" | ||||||
| 
 | 
 | ||||||
| namespace Core { | namespace Core { | ||||||
|  | struct EmuThreadHandle; | ||||||
| class ExclusiveMonitor; | class ExclusiveMonitor; | ||||||
| class System; | class System; | ||||||
| } // namespace Core
 | } // namespace Core
 | ||||||
| @ -29,8 +30,10 @@ class HandleTable; | |||||||
| class PhysicalCore; | class PhysicalCore; | ||||||
| class Process; | class Process; | ||||||
| class ResourceLimit; | class ResourceLimit; | ||||||
|  | class Scheduler; | ||||||
| class Synchronization; | class Synchronization; | ||||||
| class Thread; | class Thread; | ||||||
|  | class TimeManager; | ||||||
| 
 | 
 | ||||||
| /// Represents a single instance of the kernel.
 | /// Represents a single instance of the kernel.
 | ||||||
| class KernelCore { | class KernelCore { | ||||||
| @ -64,7 +67,7 @@ public: | |||||||
|     std::shared_ptr<ResourceLimit> GetSystemResourceLimit() const; |     std::shared_ptr<ResourceLimit> GetSystemResourceLimit() const; | ||||||
| 
 | 
 | ||||||
|     /// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table.
 |     /// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table.
 | ||||||
|     std::shared_ptr<Thread> RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const; |     std::shared_ptr<Thread> RetrieveThreadFromGlobalHandleTable(Handle handle) const; | ||||||
| 
 | 
 | ||||||
|     /// Adds the given shared pointer to an internal list of active processes.
 |     /// Adds the given shared pointer to an internal list of active processes.
 | ||||||
|     void AppendNewProcess(std::shared_ptr<Process> process); |     void AppendNewProcess(std::shared_ptr<Process> process); | ||||||
| @ -87,6 +90,12 @@ public: | |||||||
|     /// Gets the sole instance of the global scheduler
 |     /// Gets the sole instance of the global scheduler
 | ||||||
|     const Kernel::GlobalScheduler& GlobalScheduler() const; |     const Kernel::GlobalScheduler& GlobalScheduler() const; | ||||||
| 
 | 
 | ||||||
|  |     /// Gets the sole instance of the Scheduler assoviated with cpu core 'id'
 | ||||||
|  |     Kernel::Scheduler& Scheduler(std::size_t id); | ||||||
|  | 
 | ||||||
|  |     /// Gets the sole instance of the Scheduler assoviated with cpu core 'id'
 | ||||||
|  |     const Kernel::Scheduler& Scheduler(std::size_t id) const; | ||||||
|  | 
 | ||||||
|     /// Gets the an instance of the respective physical CPU core.
 |     /// Gets the an instance of the respective physical CPU core.
 | ||||||
|     Kernel::PhysicalCore& PhysicalCore(std::size_t id); |     Kernel::PhysicalCore& PhysicalCore(std::size_t id); | ||||||
| 
 | 
 | ||||||
| @ -99,6 +108,12 @@ public: | |||||||
|     /// Gets the an instance of the Synchronization Interface.
 |     /// Gets the an instance of the Synchronization Interface.
 | ||||||
|     const Kernel::Synchronization& Synchronization() const; |     const Kernel::Synchronization& Synchronization() const; | ||||||
| 
 | 
 | ||||||
|  |     /// Gets the an instance of the TimeManager Interface.
 | ||||||
|  |     Kernel::TimeManager& TimeManager(); | ||||||
|  | 
 | ||||||
|  |     /// Gets the an instance of the TimeManager Interface.
 | ||||||
|  |     const Kernel::TimeManager& TimeManager() const; | ||||||
|  | 
 | ||||||
|     /// Stops execution of 'id' core, in order to reschedule a new thread.
 |     /// Stops execution of 'id' core, in order to reschedule a new thread.
 | ||||||
|     void PrepareReschedule(std::size_t id); |     void PrepareReschedule(std::size_t id); | ||||||
| 
 | 
 | ||||||
| @ -120,6 +135,18 @@ public: | |||||||
|     /// Determines whether or not the given port is a valid named port.
 |     /// Determines whether or not the given port is a valid named port.
 | ||||||
|     bool IsValidNamedPort(NamedPortTable::const_iterator port) const; |     bool IsValidNamedPort(NamedPortTable::const_iterator port) const; | ||||||
| 
 | 
 | ||||||
|  |     /// Gets the current host_thread/guest_thread handle.
 | ||||||
|  |     Core::EmuThreadHandle GetCurrentEmuThreadID() const; | ||||||
|  | 
 | ||||||
|  |     /// Gets the current host_thread handle.
 | ||||||
|  |     u32 GetCurrentHostThreadID() const; | ||||||
|  | 
 | ||||||
|  |     /// Register the current thread as a CPU Core Thread.
 | ||||||
|  |     void RegisterCoreThread(std::size_t core_id); | ||||||
|  | 
 | ||||||
|  |     /// Register the current thread as a non CPU core thread.
 | ||||||
|  |     void RegisterHostThread(); | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|     friend class Object; |     friend class Object; | ||||||
|     friend class Process; |     friend class Process; | ||||||
| @ -140,11 +167,11 @@ private: | |||||||
|     /// Retrieves the event type used for thread wakeup callbacks.
 |     /// Retrieves the event type used for thread wakeup callbacks.
 | ||||||
|     const std::shared_ptr<Core::Timing::EventType>& ThreadWakeupCallbackEventType() const; |     const std::shared_ptr<Core::Timing::EventType>& ThreadWakeupCallbackEventType() const; | ||||||
| 
 | 
 | ||||||
|     /// Provides a reference to the thread wakeup callback handle table.
 |     /// Provides a reference to the global handle table.
 | ||||||
|     Kernel::HandleTable& ThreadWakeupCallbackHandleTable(); |     Kernel::HandleTable& GlobalHandleTable(); | ||||||
| 
 | 
 | ||||||
|     /// Provides a const reference to the thread wakeup callback handle table.
 |     /// Provides a const reference to the global handle table.
 | ||||||
|     const Kernel::HandleTable& ThreadWakeupCallbackHandleTable() const; |     const Kernel::HandleTable& GlobalHandleTable() const; | ||||||
| 
 | 
 | ||||||
|     struct Impl; |     struct Impl; | ||||||
|     std::unique_ptr<Impl> impl; |     std::unique_ptr<Impl> impl; | ||||||
|  | |||||||
| @ -18,10 +18,11 @@ | |||||||
| #include "core/hle/kernel/kernel.h" | #include "core/hle/kernel/kernel.h" | ||||||
| #include "core/hle/kernel/process.h" | #include "core/hle/kernel/process.h" | ||||||
| #include "core/hle/kernel/scheduler.h" | #include "core/hle/kernel/scheduler.h" | ||||||
|  | #include "core/hle/kernel/time_manager.h" | ||||||
| 
 | 
 | ||||||
| namespace Kernel { | namespace Kernel { | ||||||
| 
 | 
 | ||||||
| GlobalScheduler::GlobalScheduler(Core::System& system) : system{system} {} | GlobalScheduler::GlobalScheduler(KernelCore& kernel) : kernel{kernel} {} | ||||||
| 
 | 
 | ||||||
| GlobalScheduler::~GlobalScheduler() = default; | GlobalScheduler::~GlobalScheduler() = default; | ||||||
| 
 | 
 | ||||||
| @ -35,7 +36,7 @@ void GlobalScheduler::RemoveThread(std::shared_ptr<Thread> thread) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void GlobalScheduler::UnloadThread(std::size_t core) { | void GlobalScheduler::UnloadThread(std::size_t core) { | ||||||
|     Scheduler& sched = system.Scheduler(core); |     Scheduler& sched = kernel.Scheduler(core); | ||||||
|     sched.UnloadThread(); |     sched.UnloadThread(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -50,7 +51,7 @@ void GlobalScheduler::SelectThread(std::size_t core) { | |||||||
|         sched.is_context_switch_pending = sched.selected_thread != sched.current_thread; |         sched.is_context_switch_pending = sched.selected_thread != sched.current_thread; | ||||||
|         std::atomic_thread_fence(std::memory_order_seq_cst); |         std::atomic_thread_fence(std::memory_order_seq_cst); | ||||||
|     }; |     }; | ||||||
|     Scheduler& sched = system.Scheduler(core); |     Scheduler& sched = kernel.Scheduler(core); | ||||||
|     Thread* current_thread = nullptr; |     Thread* current_thread = nullptr; | ||||||
|     // Step 1: Get top thread in schedule queue.
 |     // Step 1: Get top thread in schedule queue.
 | ||||||
|     current_thread = scheduled_queue[core].empty() ? nullptr : scheduled_queue[core].front(); |     current_thread = scheduled_queue[core].empty() ? nullptr : scheduled_queue[core].front(); | ||||||
| @ -356,6 +357,32 @@ void GlobalScheduler::Shutdown() { | |||||||
|     thread_list.clear(); |     thread_list.clear(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void GlobalScheduler::Lock() { | ||||||
|  |     Core::EmuThreadHandle current_thread = kernel.GetCurrentEmuThreadID(); | ||||||
|  |     if (current_thread == current_owner) { | ||||||
|  |         ++scope_lock; | ||||||
|  |     } else { | ||||||
|  |         inner_lock.lock(); | ||||||
|  |         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; | ||||||
|  |     } | ||||||
|  |     for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { | ||||||
|  |         SelectThread(i); | ||||||
|  |     } | ||||||
|  |     current_owner = Core::EmuThreadHandle::InvalidHandle(); | ||||||
|  |     scope_lock = 1; | ||||||
|  |     inner_lock.unlock(); | ||||||
|  |     // TODO(Blinkhawk): Setup the interrupts and change context on current core.
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
| Scheduler::Scheduler(Core::System& system, Core::ARM_Interface& cpu_core, std::size_t core_id) | Scheduler::Scheduler(Core::System& system, Core::ARM_Interface& cpu_core, std::size_t core_id) | ||||||
|     : system(system), cpu_core(cpu_core), core_id(core_id) {} |     : system(system), cpu_core(cpu_core), core_id(core_id) {} | ||||||
| 
 | 
 | ||||||
| @ -485,4 +512,27 @@ void Scheduler::Shutdown() { | |||||||
|     selected_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); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } // namespace Kernel
 | } // namespace Kernel
 | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ | |||||||
| 
 | 
 | ||||||
| #include <atomic> | #include <atomic> | ||||||
| #include <memory> | #include <memory> | ||||||
|  | #include <mutex> | ||||||
| #include <vector> | #include <vector> | ||||||
| 
 | 
 | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| @ -20,11 +21,13 @@ class System; | |||||||
| 
 | 
 | ||||||
| namespace Kernel { | namespace Kernel { | ||||||
| 
 | 
 | ||||||
|  | class KernelCore; | ||||||
| class Process; | class Process; | ||||||
|  | class SchedulerLock; | ||||||
| 
 | 
 | ||||||
| class GlobalScheduler final { | class GlobalScheduler final { | ||||||
| public: | public: | ||||||
|     explicit GlobalScheduler(Core::System& system); |     explicit GlobalScheduler(KernelCore& kernel); | ||||||
|     ~GlobalScheduler(); |     ~GlobalScheduler(); | ||||||
| 
 | 
 | ||||||
|     /// Adds a new thread to the scheduler
 |     /// Adds a new thread to the scheduler
 | ||||||
| @ -138,6 +141,14 @@ public: | |||||||
|     void Shutdown(); |     void Shutdown(); | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|  |     friend class SchedulerLock; | ||||||
|  | 
 | ||||||
|  |     /// Lock the scheduler to the current thread.
 | ||||||
|  |     void Lock(); | ||||||
|  | 
 | ||||||
|  |     /// Unlocks the scheduler, reselects threads, interrupts cores for rescheduling
 | ||||||
|  |     /// and reschedules current core if needed.
 | ||||||
|  |     void Unlock(); | ||||||
|     /**
 |     /**
 | ||||||
|      * Transfers a thread into an specific core. If the destination_core is -1 |      * Transfers a thread into an specific core. If the destination_core is -1 | ||||||
|      * it will be unscheduled from its source code and added into its suggested |      * it will be unscheduled from its source code and added into its suggested | ||||||
| @ -158,9 +169,14 @@ private: | |||||||
|     // ordered from Core 0 to Core 3.
 |     // ordered from Core 0 to Core 3.
 | ||||||
|     std::array<u32, Core::Hardware::NUM_CPU_CORES> preemption_priorities = {59, 59, 59, 62}; |     std::array<u32, Core::Hardware::NUM_CPU_CORES> preemption_priorities = {59, 59, 59, 62}; | ||||||
| 
 | 
 | ||||||
|  |     /// Scheduler lock mechanisms.
 | ||||||
|  |     std::mutex inner_lock{}; // TODO(Blinkhawk): Replace for a SpinLock
 | ||||||
|  |     std::atomic<s64> scope_lock{}; | ||||||
|  |     Core::EmuThreadHandle current_owner{Core::EmuThreadHandle::InvalidHandle()}; | ||||||
|  | 
 | ||||||
|     /// Lists all thread ids that aren't deleted/etc.
 |     /// Lists all thread ids that aren't deleted/etc.
 | ||||||
|     std::vector<std::shared_ptr<Thread>> thread_list; |     std::vector<std::shared_ptr<Thread>> thread_list; | ||||||
|     Core::System& system; |     KernelCore& kernel; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class Scheduler final { | class Scheduler final { | ||||||
| @ -227,4 +243,30 @@ private: | |||||||
|     bool is_context_switch_pending = false; |     bool is_context_switch_pending = false; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | class SchedulerLock { | ||||||
|  | public: | ||||||
|  |     explicit SchedulerLock(KernelCore& kernel); | ||||||
|  |     ~SchedulerLock(); | ||||||
|  | 
 | ||||||
|  | protected: | ||||||
|  |     KernelCore& kernel; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class SchedulerLockAndSleep : public SchedulerLock { | ||||||
|  | public: | ||||||
|  |     explicit SchedulerLockAndSleep(KernelCore& kernel, Handle& event_handle, Thread* time_task, | ||||||
|  |                                    s64 nanoseconds); | ||||||
|  |     ~SchedulerLockAndSleep(); | ||||||
|  | 
 | ||||||
|  |     void CancelSleep() { | ||||||
|  |         sleep_cancelled = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     Handle& event_handle; | ||||||
|  |     Thread* time_task; | ||||||
|  |     s64 nanoseconds; | ||||||
|  |     bool sleep_cancelled{}; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| } // namespace Kernel
 | } // namespace Kernel
 | ||||||
|  | |||||||
| @ -46,9 +46,9 @@ Thread::~Thread() = default; | |||||||
| void Thread::Stop() { | void Thread::Stop() { | ||||||
|     // Cancel any outstanding wakeup events for this thread
 |     // Cancel any outstanding wakeup events for this thread
 | ||||||
|     Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), |     Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), | ||||||
|                                                              callback_handle); |                                                              global_handle); | ||||||
|     kernel.ThreadWakeupCallbackHandleTable().Close(callback_handle); |     kernel.GlobalHandleTable().Close(global_handle); | ||||||
|     callback_handle = 0; |     global_handle = 0; | ||||||
|     SetStatus(ThreadStatus::Dead); |     SetStatus(ThreadStatus::Dead); | ||||||
|     Signal(); |     Signal(); | ||||||
| 
 | 
 | ||||||
| @ -73,12 +73,12 @@ void Thread::WakeAfterDelay(s64 nanoseconds) { | |||||||
|     // thread-safe version of ScheduleEvent.
 |     // thread-safe version of ScheduleEvent.
 | ||||||
|     const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds}); |     const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds}); | ||||||
|     Core::System::GetInstance().CoreTiming().ScheduleEvent( |     Core::System::GetInstance().CoreTiming().ScheduleEvent( | ||||||
|         cycles, kernel.ThreadWakeupCallbackEventType(), callback_handle); |         cycles, kernel.ThreadWakeupCallbackEventType(), global_handle); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Thread::CancelWakeupTimer() { | void Thread::CancelWakeupTimer() { | ||||||
|     Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), |     Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), | ||||||
|                                                              callback_handle); |                                                              global_handle); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Thread::ResumeFromWait() { | void Thread::ResumeFromWait() { | ||||||
| @ -190,7 +190,7 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::strin | |||||||
|     thread->condvar_wait_address = 0; |     thread->condvar_wait_address = 0; | ||||||
|     thread->wait_handle = 0; |     thread->wait_handle = 0; | ||||||
|     thread->name = std::move(name); |     thread->name = std::move(name); | ||||||
|     thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap(); |     thread->global_handle = kernel.GlobalHandleTable().Create(thread).Unwrap(); | ||||||
|     thread->owner_process = &owner_process; |     thread->owner_process = &owner_process; | ||||||
|     auto& scheduler = kernel.GlobalScheduler(); |     auto& scheduler = kernel.GlobalScheduler(); | ||||||
|     scheduler.AddThread(thread); |     scheduler.AddThread(thread); | ||||||
|  | |||||||
| @ -453,6 +453,10 @@ public: | |||||||
|         is_sync_cancelled = value; |         is_sync_cancelled = value; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     Handle GetGlobalHandle() const { | ||||||
|  |         return global_handle; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
| private: | private: | ||||||
|     void SetSchedulingStatus(ThreadSchedStatus new_status); |     void SetSchedulingStatus(ThreadSchedStatus new_status); | ||||||
|     void SetCurrentPriority(u32 new_priority); |     void SetCurrentPriority(u32 new_priority); | ||||||
| @ -514,7 +518,7 @@ private: | |||||||
|     VAddr arb_wait_address{0}; |     VAddr arb_wait_address{0}; | ||||||
| 
 | 
 | ||||||
|     /// Handle used as userdata to reference this object when inserting into the CoreTiming queue.
 |     /// Handle used as userdata to reference this object when inserting into the CoreTiming queue.
 | ||||||
|     Handle callback_handle = 0; |     Handle global_handle = 0; | ||||||
| 
 | 
 | ||||||
|     /// Callback that will be invoked when the thread is resumed from a waiting state. If the thread
 |     /// Callback that will be invoked when the thread is resumed from a waiting state. If the thread
 | ||||||
|     /// was waiting via WaitSynchronization then the object will be the last object that became
 |     /// was waiting via WaitSynchronization then the object will be the last object that became
 | ||||||
|  | |||||||
							
								
								
									
										44
									
								
								src/core/hle/kernel/time_manager.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/core/hle/kernel/time_manager.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,44 @@ | |||||||
|  | // Copyright 2020 yuzu Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #include "common/assert.h" | ||||||
|  | #include "core/core.h" | ||||||
|  | #include "core/core_timing.h" | ||||||
|  | #include "core/core_timing_util.h" | ||||||
|  | #include "core/hle/kernel/handle_table.h" | ||||||
|  | #include "core/hle/kernel/kernel.h" | ||||||
|  | #include "core/hle/kernel/thread.h" | ||||||
|  | #include "core/hle/kernel/time_manager.h" | ||||||
|  | 
 | ||||||
|  | namespace Kernel { | ||||||
|  | 
 | ||||||
|  | TimeManager::TimeManager(Core::System& system) : system{system} { | ||||||
|  |     time_manager_event_type = Core::Timing::CreateEvent( | ||||||
|  |         "Kernel::TimeManagerCallback", [this](u64 thread_handle, [[maybe_unused]] s64 cycles_late) { | ||||||
|  |             Handle proper_handle = static_cast<Handle>(thread_handle); | ||||||
|  |             std::shared_ptr<Thread> thread = | ||||||
|  |                 this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle); | ||||||
|  |             thread->ResumeFromWait(); | ||||||
|  |         }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void TimeManager::ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 nanoseconds) { | ||||||
|  |     if (nanoseconds > 0) { | ||||||
|  |         ASSERT(timetask); | ||||||
|  |         event_handle = timetask->GetGlobalHandle(); | ||||||
|  |         const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds}); | ||||||
|  |         system.CoreTiming().ScheduleEvent(cycles, time_manager_event_type, event_handle); | ||||||
|  |     } else { | ||||||
|  |         event_handle = InvalidHandle; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void TimeManager::UnscheduleTimeEvent(Handle event_handle) { | ||||||
|  |     if (event_handle == InvalidHandle) { | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     system.CoreTiming().UnscheduleEvent(time_manager_event_type, event_handle); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace Kernel
 | ||||||
							
								
								
									
										43
									
								
								src/core/hle/kernel/time_manager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/core/hle/kernel/time_manager.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | |||||||
|  | // Copyright 2020 yuzu Emulator Project
 | ||||||
|  | // Licensed under GPLv2 or any later version
 | ||||||
|  | // Refer to the license.txt file included.
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <memory> | ||||||
|  | 
 | ||||||
|  | #include "core/hle/kernel/object.h" | ||||||
|  | 
 | ||||||
|  | namespace Core { | ||||||
|  | class System; | ||||||
|  | } // namespace Core
 | ||||||
|  | 
 | ||||||
|  | namespace Core::Timing { | ||||||
|  | struct EventType; | ||||||
|  | } // namespace Core::Timing
 | ||||||
|  | 
 | ||||||
|  | namespace Kernel { | ||||||
|  | 
 | ||||||
|  | class Thread; | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * The `TimeManager` takes care of scheduling time events on threads and executes their TimeUp | ||||||
|  |  * method when the event is triggered. | ||||||
|  |  */ | ||||||
|  | class TimeManager { | ||||||
|  | public: | ||||||
|  |     explicit TimeManager(Core::System& system); | ||||||
|  | 
 | ||||||
|  |     /// Schedule a time event on `timetask` thread that will expire in 'nanoseconds'
 | ||||||
|  |     /// returns a non-invalid handle in `event_handle` if correctly scheduled
 | ||||||
|  |     void ScheduleTimeEvent(Handle& event_handle, Thread* timetask, s64 nanoseconds); | ||||||
|  | 
 | ||||||
|  |     /// Unschedule an existing time event
 | ||||||
|  |     void UnscheduleTimeEvent(Handle event_handle); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     Core::System& system; | ||||||
|  |     std::shared_ptr<Core::Timing::EventType> time_manager_event_type; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace Kernel
 | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user