Kernel: Initial implementation of thread preemption.
This commit is contained in:
parent
103f3a2fe5
commit
b49c0dab87
|
@ -12,6 +12,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/hle/kernel/address_arbiter.h"
|
#include "core/hle/kernel/address_arbiter.h"
|
||||||
#include "core/hle/kernel/client_port.h"
|
#include "core/hle/kernel/client_port.h"
|
||||||
#include "core/hle/kernel/handle_table.h"
|
#include "core/hle/kernel/handle_table.h"
|
||||||
|
@ -96,6 +97,7 @@ struct KernelCore::Impl {
|
||||||
|
|
||||||
InitializeSystemResourceLimit(kernel);
|
InitializeSystemResourceLimit(kernel);
|
||||||
InitializeThreads();
|
InitializeThreads();
|
||||||
|
InitializePreemption();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shutdown() {
|
void Shutdown() {
|
||||||
|
@ -111,6 +113,7 @@ struct KernelCore::Impl {
|
||||||
|
|
||||||
thread_wakeup_callback_handle_table.Clear();
|
thread_wakeup_callback_handle_table.Clear();
|
||||||
thread_wakeup_event_type = nullptr;
|
thread_wakeup_event_type = nullptr;
|
||||||
|
preemption_event = nullptr;
|
||||||
|
|
||||||
named_ports.clear();
|
named_ports.clear();
|
||||||
}
|
}
|
||||||
|
@ -133,6 +136,18 @@ struct KernelCore::Impl {
|
||||||
system.CoreTiming().RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
|
system.CoreTiming().RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InitializePreemption() {
|
||||||
|
preemption_event = system.CoreTiming().RegisterEvent(
|
||||||
|
"PreemptionCallback", [this](u64 userdata, s64 cycles_late) {
|
||||||
|
global_scheduler.PreemptThreads();
|
||||||
|
s64 time_interval = Core::Timing::msToCycles(std::chrono::milliseconds(10));
|
||||||
|
system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
|
||||||
|
});
|
||||||
|
|
||||||
|
s64 time_interval = Core::Timing::msToCycles(std::chrono::milliseconds(10));
|
||||||
|
system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
|
||||||
|
}
|
||||||
|
|
||||||
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};
|
||||||
|
@ -146,6 +161,7 @@ struct KernelCore::Impl {
|
||||||
SharedPtr<ResourceLimit> system_resource_limit;
|
SharedPtr<ResourceLimit> system_resource_limit;
|
||||||
|
|
||||||
Core::Timing::EventType* thread_wakeup_event_type = nullptr;
|
Core::Timing::EventType* thread_wakeup_event_type = nullptr;
|
||||||
|
Core::Timing::EventType* preemption_event = nullptr;
|
||||||
// TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future,
|
// TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future,
|
||||||
// allowing us to simply use a pool index or similar.
|
// allowing us to simply use a pool index or similar.
|
||||||
Kernel::HandleTable thread_wakeup_callback_handle_table;
|
Kernel::HandleTable thread_wakeup_callback_handle_table;
|
||||||
|
|
|
@ -238,6 +238,16 @@ bool GlobalScheduler::YieldThreadAndWaitForLoadBalancing(Thread* yielding_thread
|
||||||
return AskForReselectionOrMarkRedundant(yielding_thread, winner);
|
return AskForReselectionOrMarkRedundant(yielding_thread, winner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GlobalScheduler::PreemptThreads() {
|
||||||
|
for (std::size_t core_id = 0; core_id < NUM_CPU_CORES; core_id++) {
|
||||||
|
const u64 priority = preemption_priorities[core_id];
|
||||||
|
if (scheduled_queue[core_id].size(priority) > 1) {
|
||||||
|
scheduled_queue[core_id].yield(priority);
|
||||||
|
reselection_pending.store(true, std::memory_order_release);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GlobalScheduler::Schedule(u32 priority, u32 core, Thread* thread) {
|
void GlobalScheduler::Schedule(u32 priority, u32 core, Thread* thread) {
|
||||||
ASSERT_MSG(thread->GetProcessorID() == core, "Thread must be assigned to this core.");
|
ASSERT_MSG(thread->GetProcessorID() == core, "Thread must be assigned to this core.");
|
||||||
scheduled_queue[core].add(thread, priority);
|
scheduled_queue[core].add(thread, priority);
|
||||||
|
|
|
@ -133,6 +133,8 @@ public:
|
||||||
*/
|
*/
|
||||||
bool YieldThreadAndWaitForLoadBalancing(Thread* thread);
|
bool YieldThreadAndWaitForLoadBalancing(Thread* thread);
|
||||||
|
|
||||||
|
void PreemptThreads();
|
||||||
|
|
||||||
u32 CpuCoresCount() const {
|
u32 CpuCoresCount() const {
|
||||||
return NUM_CPU_CORES;
|
return NUM_CPU_CORES;
|
||||||
}
|
}
|
||||||
|
@ -153,6 +155,8 @@ private:
|
||||||
std::array<Common::MultiLevelQueue<Thread*, THREADPRIO_COUNT>, NUM_CPU_CORES> suggested_queue;
|
std::array<Common::MultiLevelQueue<Thread*, THREADPRIO_COUNT>, NUM_CPU_CORES> suggested_queue;
|
||||||
std::atomic<bool> reselection_pending;
|
std::atomic<bool> reselection_pending;
|
||||||
|
|
||||||
|
std::array<u64, NUM_CPU_CORES> preemption_priorities = {59, 59, 59, 62};
|
||||||
|
|
||||||
/// Lists all thread ids that aren't deleted/etc.
|
/// Lists all thread ids that aren't deleted/etc.
|
||||||
std::vector<SharedPtr<Thread>> thread_list;
|
std::vector<SharedPtr<Thread>> thread_list;
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
|
|
Loading…
Reference in New Issue