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…
Reference in a new issue