summaryrefslogtreecommitdiff
path: root/src/core/hle/kernel
diff options
context:
space:
mode:
authorGravatar Fernando Sahmkow2020-02-24 22:04:12 -0400
committerGravatar Fernando Sahmkow2020-06-27 11:35:06 -0400
commite31425df3877636c098ec7426ebd2067920715cb (patch)
tree5c0fc518a4ebb8413c491b43a9fdd99450c7bd80 /src/core/hle/kernel
parentMerge pull request #3396 from FernandoS27/prometheus-1 (diff)
downloadyuzu-e31425df3877636c098ec7426ebd2067920715cb.tar.gz
yuzu-e31425df3877636c098ec7426ebd2067920715cb.tar.xz
yuzu-e31425df3877636c098ec7426ebd2067920715cb.zip
General: Recover Prometheus project from harddrive failure
This commit: Implements CPU Interrupts, Replaces Cycle Timing for Host Timing, Reworks the Kernel's Scheduler, Introduce Idle State and Suspended State, Recreates the bootmanager, Initializes Multicore system.
Diffstat (limited to 'src/core/hle/kernel')
-rw-r--r--src/core/hle/kernel/kernel.cpp84
-rw-r--r--src/core/hle/kernel/kernel.h19
-rw-r--r--src/core/hle/kernel/physical_core.cpp37
-rw-r--r--src/core/hle/kernel/physical_core.h21
-rw-r--r--src/core/hle/kernel/process.cpp17
-rw-r--r--src/core/hle/kernel/scheduler.cpp415
-rw-r--r--src/core/hle/kernel/scheduler.h94
-rw-r--r--src/core/hle/kernel/svc.cpp21
-rw-r--r--src/core/hle/kernel/thread.cpp232
-rw-r--r--src/core/hle/kernel/thread.h81
-rw-r--r--src/core/hle/kernel/time_manager.cpp2
11 files changed, 679 insertions, 344 deletions
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 7655382fa..ba051a7d8 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -13,11 +13,13 @@
13 13
14#include "common/assert.h" 14#include "common/assert.h"
15#include "common/logging/log.h" 15#include "common/logging/log.h"
16#include "common/thread.h"
16#include "core/arm/arm_interface.h" 17#include "core/arm/arm_interface.h"
17#include "core/arm/exclusive_monitor.h" 18#include "core/arm/exclusive_monitor.h"
18#include "core/core.h" 19#include "core/core.h"
19#include "core/core_timing.h" 20#include "core/core_timing.h"
20#include "core/core_timing_util.h" 21#include "core/core_timing_util.h"
22#include "core/cpu_manager.h"
21#include "core/device_memory.h" 23#include "core/device_memory.h"
22#include "core/hardware_properties.h" 24#include "core/hardware_properties.h"
23#include "core/hle/kernel/client_port.h" 25#include "core/hle/kernel/client_port.h"
@@ -117,7 +119,9 @@ struct KernelCore::Impl {
117 InitializeSystemResourceLimit(kernel); 119 InitializeSystemResourceLimit(kernel);
118 InitializeMemoryLayout(); 120 InitializeMemoryLayout();
119 InitializeThreads(); 121 InitializeThreads();
120 InitializePreemption(); 122 InitializePreemption(kernel);
123 InitializeSchedulers();
124 InitializeSuspendThreads();
121 } 125 }
122 126
123 void Shutdown() { 127 void Shutdown() {
@@ -155,6 +159,12 @@ struct KernelCore::Impl {
155 } 159 }
156 } 160 }
157 161
162 void InitializeSchedulers() {
163 for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
164 cores[i].Scheduler().Initialize();
165 }
166 }
167
158 // Creates the default system resource limit 168 // Creates the default system resource limit
159 void InitializeSystemResourceLimit(KernelCore& kernel) { 169 void InitializeSystemResourceLimit(KernelCore& kernel) {
160 system_resource_limit = ResourceLimit::Create(kernel); 170 system_resource_limit = ResourceLimit::Create(kernel);
@@ -178,10 +188,13 @@ struct KernelCore::Impl {
178 Core::Timing::CreateEvent("ThreadWakeupCallback", ThreadWakeupCallback); 188 Core::Timing::CreateEvent("ThreadWakeupCallback", ThreadWakeupCallback);
179 } 189 }
180 190
181 void InitializePreemption() { 191 void InitializePreemption(KernelCore& kernel) {
182 preemption_event = 192 preemption_event = Core::Timing::CreateEvent(
183 Core::Timing::CreateEvent("PreemptionCallback", [this](u64 userdata, s64 cycles_late) { 193 "PreemptionCallback", [this, &kernel](u64 userdata, s64 cycles_late) {
184 global_scheduler.PreemptThreads(); 194 {
195 SchedulerLock lock(kernel);
196 global_scheduler.PreemptThreads();
197 }
185 s64 time_interval = Core::Timing::msToCycles(std::chrono::milliseconds(10)); 198 s64 time_interval = Core::Timing::msToCycles(std::chrono::milliseconds(10));
186 system.CoreTiming().ScheduleEvent(time_interval, preemption_event); 199 system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
187 }); 200 });
@@ -190,6 +203,20 @@ struct KernelCore::Impl {
190 system.CoreTiming().ScheduleEvent(time_interval, preemption_event); 203 system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
191 } 204 }
192 205
206 void InitializeSuspendThreads() {
207 for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
208 std::string name = "Suspend Thread Id:" + std::to_string(i);
209 std::function<void(void*)> init_func =
210 system.GetCpuManager().GetSuspendThreadStartFunc();
211 void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
212 ThreadType type =
213 static_cast<ThreadType>(THREADTYPE_KERNEL | THREADTYPE_HLE | THREADTYPE_SUSPEND);
214 auto thread_res = Thread::Create(system, type, name, 0, 0, 0, static_cast<u32>(i), 0,
215 nullptr, std::move(init_func), init_func_parameter);
216 suspend_threads[i] = std::move(thread_res).Unwrap();
217 }
218 }
219
193 void MakeCurrentProcess(Process* process) { 220 void MakeCurrentProcess(Process* process) {
194 current_process = process; 221 current_process = process;
195 222
@@ -201,7 +228,10 @@ struct KernelCore::Impl {
201 core.SetIs64Bit(process->Is64BitProcess()); 228 core.SetIs64Bit(process->Is64BitProcess());
202 } 229 }
203 230
204 system.Memory().SetCurrentPageTable(*process); 231 u32 core_id = GetCurrentHostThreadID();
232 if (core_id < Core::Hardware::NUM_CPU_CORES) {
233 system.Memory().SetCurrentPageTable(*process, core_id);
234 }
205 } 235 }
206 236
207 void RegisterCoreThread(std::size_t core_id) { 237 void RegisterCoreThread(std::size_t core_id) {
@@ -219,7 +249,9 @@ struct KernelCore::Impl {
219 std::unique_lock lock{register_thread_mutex}; 249 std::unique_lock lock{register_thread_mutex};
220 const std::thread::id this_id = std::this_thread::get_id(); 250 const std::thread::id this_id = std::this_thread::get_id();
221 const auto it = host_thread_ids.find(this_id); 251 const auto it = host_thread_ids.find(this_id);
222 ASSERT(it == host_thread_ids.end()); 252 if (it != host_thread_ids.end()) {
253 return;
254 }
223 host_thread_ids[this_id] = registered_thread_ids++; 255 host_thread_ids[this_id] = registered_thread_ids++;
224 } 256 }
225 257
@@ -343,6 +375,8 @@ struct KernelCore::Impl {
343 std::shared_ptr<Kernel::SharedMemory> irs_shared_mem; 375 std::shared_ptr<Kernel::SharedMemory> irs_shared_mem;
344 std::shared_ptr<Kernel::SharedMemory> time_shared_mem; 376 std::shared_ptr<Kernel::SharedMemory> time_shared_mem;
345 377
378 std::array<std::shared_ptr<Thread>, Core::Hardware::NUM_CPU_CORES> suspend_threads{};
379
346 // System context 380 // System context
347 Core::System& system; 381 Core::System& system;
348}; 382};
@@ -412,6 +446,26 @@ const Kernel::PhysicalCore& KernelCore::PhysicalCore(std::size_t id) const {
412 return impl->cores[id]; 446 return impl->cores[id];
413} 447}
414 448
449Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() {
450 u32 core_id = impl->GetCurrentHostThreadID();
451 ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
452 return impl->cores[core_id];
453}
454
455const Kernel::PhysicalCore& KernelCore::CurrentPhysicalCore() const {
456 u32 core_id = impl->GetCurrentHostThreadID();
457 ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
458 return impl->cores[core_id];
459}
460
461Kernel::Scheduler& KernelCore::CurrentScheduler() {
462 return CurrentPhysicalCore().Scheduler();
463}
464
465const Kernel::Scheduler& KernelCore::CurrentScheduler() const {
466 return CurrentPhysicalCore().Scheduler();
467}
468
415Kernel::Synchronization& KernelCore::Synchronization() { 469Kernel::Synchronization& KernelCore::Synchronization() {
416 return impl->synchronization; 470 return impl->synchronization;
417} 471}
@@ -557,4 +611,20 @@ const Kernel::SharedMemory& KernelCore::GetTimeSharedMem() const {
557 return *impl->time_shared_mem; 611 return *impl->time_shared_mem;
558} 612}
559 613
614void KernelCore::Suspend(bool in_suspention) {
615 const bool should_suspend = exception_exited || in_suspention;
616 {
617 SchedulerLock lock(*this);
618 ThreadStatus status = should_suspend ? ThreadStatus::Ready : ThreadStatus::WaitSleep;
619 for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) {
620 impl->suspend_threads[i]->SetStatus(status);
621 }
622 }
623}
624
625void KernelCore::ExceptionalExit() {
626 exception_exited = true;
627 Suspend(true);
628}
629
560} // namespace Kernel 630} // namespace Kernel
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 83de1f542..5d32a8329 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -110,6 +110,18 @@ public:
110 /// Gets the an instance of the respective physical CPU core. 110 /// Gets the an instance of the respective physical CPU core.
111 const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const; 111 const Kernel::PhysicalCore& PhysicalCore(std::size_t id) const;
112 112
113 /// Gets the sole instance of the Scheduler at the current running core.
114 Kernel::Scheduler& CurrentScheduler();
115
116 /// Gets the sole instance of the Scheduler at the current running core.
117 const Kernel::Scheduler& CurrentScheduler() const;
118
119 /// Gets the an instance of the current physical CPU core.
120 Kernel::PhysicalCore& CurrentPhysicalCore();
121
122 /// Gets the an instance of the current physical CPU core.
123 const Kernel::PhysicalCore& CurrentPhysicalCore() const;
124
113 /// Gets the an instance of the Synchronization Interface. 125 /// Gets the an instance of the Synchronization Interface.
114 Kernel::Synchronization& Synchronization(); 126 Kernel::Synchronization& Synchronization();
115 127
@@ -191,6 +203,12 @@ public:
191 /// Gets the shared memory object for Time services. 203 /// Gets the shared memory object for Time services.
192 const Kernel::SharedMemory& GetTimeSharedMem() const; 204 const Kernel::SharedMemory& GetTimeSharedMem() const;
193 205
206 /// Suspend/unsuspend the OS.
207 void Suspend(bool in_suspention);
208
209 /// Exceptional exit the OS.
210 void ExceptionalExit();
211
194private: 212private:
195 friend class Object; 213 friend class Object;
196 friend class Process; 214 friend class Process;
@@ -219,6 +237,7 @@ private:
219 237
220 struct Impl; 238 struct Impl;
221 std::unique_ptr<Impl> impl; 239 std::unique_ptr<Impl> impl;
240 bool exception_exited{};
222}; 241};
223 242
224} // namespace Kernel 243} // namespace Kernel
diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp
index a15011076..69202540b 100644
--- a/src/core/hle/kernel/physical_core.cpp
+++ b/src/core/hle/kernel/physical_core.cpp
@@ -2,12 +2,15 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/assert.h"
5#include "common/logging/log.h" 6#include "common/logging/log.h"
7#include "common/spin_lock.h"
6#include "core/arm/arm_interface.h" 8#include "core/arm/arm_interface.h"
7#ifdef ARCHITECTURE_x86_64 9#ifdef ARCHITECTURE_x86_64
8#include "core/arm/dynarmic/arm_dynarmic_32.h" 10#include "core/arm/dynarmic/arm_dynarmic_32.h"
9#include "core/arm/dynarmic/arm_dynarmic_64.h" 11#include "core/arm/dynarmic/arm_dynarmic_64.h"
10#endif 12#endif
13#include "core/arm/cpu_interrupt_handler.h"
11#include "core/arm/exclusive_monitor.h" 14#include "core/arm/exclusive_monitor.h"
12#include "core/arm/unicorn/arm_unicorn.h" 15#include "core/arm/unicorn/arm_unicorn.h"
13#include "core/core.h" 16#include "core/core.h"
@@ -19,21 +22,23 @@ namespace Kernel {
19 22
20PhysicalCore::PhysicalCore(Core::System& system, std::size_t id, 23PhysicalCore::PhysicalCore(Core::System& system, std::size_t id,
21 Core::ExclusiveMonitor& exclusive_monitor) 24 Core::ExclusiveMonitor& exclusive_monitor)
22 : core_index{id} { 25 : interrupt_handler{}, core_index{id} {
23#ifdef ARCHITECTURE_x86_64 26#ifdef ARCHITECTURE_x86_64
24 arm_interface_32 = 27 arm_interface_32 = std::make_unique<Core::ARM_Dynarmic_32>(system, interrupt_handler,
25 std::make_unique<Core::ARM_Dynarmic_32>(system, exclusive_monitor, core_index); 28 exclusive_monitor, core_index);
26 arm_interface_64 = 29 arm_interface_64 = std::make_unique<Core::ARM_Dynarmic_64>(system, interrupt_handler,
27 std::make_unique<Core::ARM_Dynarmic_64>(system, exclusive_monitor, core_index); 30 exclusive_monitor, core_index);
28
29#else 31#else
30 using Core::ARM_Unicorn; 32 using Core::ARM_Unicorn;
31 arm_interface_32 = std::make_unique<ARM_Unicorn>(system, ARM_Unicorn::Arch::AArch32); 33 arm_interface_32 =
32 arm_interface_64 = std::make_unique<ARM_Unicorn>(system, ARM_Unicorn::Arch::AArch64); 34 std::make_unique<ARM_Unicorn>(system, interrupt_handler, ARM_Unicorn::Arch::AArch32);
35 arm_interface_64 =
36 std::make_unique<ARM_Unicorn>(system, interrupt_handler, ARM_Unicorn::Arch::AArch64);
33 LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); 37 LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
34#endif 38#endif
35 39
36 scheduler = std::make_unique<Kernel::Scheduler>(system, core_index); 40 scheduler = std::make_unique<Kernel::Scheduler>(system, core_index);
41 guard = std::make_unique<Common::SpinLock>();
37} 42}
38 43
39PhysicalCore::~PhysicalCore() = default; 44PhysicalCore::~PhysicalCore() = default;
@@ -47,6 +52,10 @@ void PhysicalCore::Step() {
47 arm_interface->Step(); 52 arm_interface->Step();
48} 53}
49 54
55void PhysicalCore::Idle() {
56 interrupt_handler.AwaitInterrupt();
57}
58
50void PhysicalCore::Stop() { 59void PhysicalCore::Stop() {
51 arm_interface->PrepareReschedule(); 60 arm_interface->PrepareReschedule();
52} 61}
@@ -63,4 +72,16 @@ void PhysicalCore::SetIs64Bit(bool is_64_bit) {
63 } 72 }
64} 73}
65 74
75void PhysicalCore::Interrupt() {
76 guard->lock();
77 interrupt_handler.SetInterrupt(true);
78 guard->unlock();
79}
80
81void PhysicalCore::ClearInterrupt() {
82 guard->lock();
83 interrupt_handler.SetInterrupt(false);
84 guard->unlock();
85}
86
66} // namespace Kernel 87} // namespace Kernel
diff --git a/src/core/hle/kernel/physical_core.h b/src/core/hle/kernel/physical_core.h
index 3269166be..c3da30b72 100644
--- a/src/core/hle/kernel/physical_core.h
+++ b/src/core/hle/kernel/physical_core.h
@@ -7,6 +7,12 @@
7#include <cstddef> 7#include <cstddef>
8#include <memory> 8#include <memory>
9 9
10#include "core/arm/cpu_interrupt_handler.h"
11
12namespace Common {
13 class SpinLock;
14}
15
10namespace Kernel { 16namespace Kernel {
11class Scheduler; 17class Scheduler;
12} // namespace Kernel 18} // namespace Kernel
@@ -32,11 +38,24 @@ public:
32 38
33 /// Execute current jit state 39 /// Execute current jit state
34 void Run(); 40 void Run();
41 /// Set this core in IdleState.
42 void Idle();
35 /// Execute a single instruction in current jit. 43 /// Execute a single instruction in current jit.
36 void Step(); 44 void Step();
37 /// Stop JIT execution/exit 45 /// Stop JIT execution/exit
38 void Stop(); 46 void Stop();
39 47
48 /// Interrupt this physical core.
49 void Interrupt();
50
51 /// Clear this core's interrupt
52 void ClearInterrupt();
53
54 /// Check if this core is interrupted
55 bool IsInterrupted() const {
56 return interrupt_handler.IsInterrupted();
57 }
58
40 // Shutdown this physical core. 59 // Shutdown this physical core.
41 void Shutdown(); 60 void Shutdown();
42 61
@@ -71,11 +90,13 @@ public:
71 void SetIs64Bit(bool is_64_bit); 90 void SetIs64Bit(bool is_64_bit);
72 91
73private: 92private:
93 Core::CPUInterruptHandler interrupt_handler;
74 std::size_t core_index; 94 std::size_t core_index;
75 std::unique_ptr<Core::ARM_Interface> arm_interface_32; 95 std::unique_ptr<Core::ARM_Interface> arm_interface_32;
76 std::unique_ptr<Core::ARM_Interface> arm_interface_64; 96 std::unique_ptr<Core::ARM_Interface> arm_interface_64;
77 std::unique_ptr<Kernel::Scheduler> scheduler; 97 std::unique_ptr<Kernel::Scheduler> scheduler;
78 Core::ARM_Interface* arm_interface{}; 98 Core::ARM_Interface* arm_interface{};
99 std::unique_ptr<Common::SpinLock> guard;
79}; 100};
80 101
81} // namespace Kernel 102} // namespace Kernel
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index c4c5199b1..7e26a54f4 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -30,14 +30,15 @@ namespace {
30/** 30/**
31 * Sets up the primary application thread 31 * Sets up the primary application thread
32 * 32 *
33 * @param system The system instance to create the main thread under.
33 * @param owner_process The parent process for the main thread 34 * @param owner_process The parent process for the main thread
34 * @param kernel The kernel instance to create the main thread under.
35 * @param priority The priority to give the main thread 35 * @param priority The priority to give the main thread
36 */ 36 */
37void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority, VAddr stack_top) { 37void SetupMainThread(Core::System& system, Process& owner_process, u32 priority, VAddr stack_top) {
38 const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart(); 38 const VAddr entry_point = owner_process.PageTable().GetCodeRegionStart();
39 auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, 39 ThreadType type = THREADTYPE_USER;
40 owner_process.GetIdealCore(), stack_top, owner_process); 40 auto thread_res = Thread::Create(system, type, "main", entry_point, priority, 0,
41 owner_process.GetIdealCore(), stack_top, &owner_process);
41 42
42 std::shared_ptr<Thread> thread = std::move(thread_res).Unwrap(); 43 std::shared_ptr<Thread> thread = std::move(thread_res).Unwrap();
43 44
@@ -48,8 +49,12 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority, V
48 thread->GetContext32().cpu_registers[1] = thread_handle; 49 thread->GetContext32().cpu_registers[1] = thread_handle;
49 thread->GetContext64().cpu_registers[1] = thread_handle; 50 thread->GetContext64().cpu_registers[1] = thread_handle;
50 51
52 auto& kernel = system.Kernel();
51 // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires 53 // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
52 thread->ResumeFromWait(); 54 {
55 SchedulerLock lock{kernel};
56 thread->SetStatus(ThreadStatus::Ready);
57 }
53} 58}
54} // Anonymous namespace 59} // Anonymous namespace
55 60
@@ -294,7 +299,7 @@ void Process::Run(s32 main_thread_priority, u64 stack_size) {
294 299
295 ChangeStatus(ProcessStatus::Running); 300 ChangeStatus(ProcessStatus::Running);
296 301
297 SetupMainThread(*this, kernel, main_thread_priority, main_thread_stack_top); 302 SetupMainThread(system, *this, main_thread_priority, main_thread_stack_top);
298 resource_limit->Reserve(ResourceType::Threads, 1); 303 resource_limit->Reserve(ResourceType::Threads, 1);
299 resource_limit->Reserve(ResourceType::PhysicalMemory, main_thread_stack_size); 304 resource_limit->Reserve(ResourceType::PhysicalMemory, main_thread_stack_size);
300} 305}
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index 1140c72a3..5166020a0 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -11,11 +11,15 @@
11#include <utility> 11#include <utility>
12 12
13#include "common/assert.h" 13#include "common/assert.h"
14#include "common/bit_util.h"
15#include "common/fiber.h"
14#include "common/logging/log.h" 16#include "common/logging/log.h"
15#include "core/arm/arm_interface.h" 17#include "core/arm/arm_interface.h"
16#include "core/core.h" 18#include "core/core.h"
17#include "core/core_timing.h" 19#include "core/core_timing.h"
20#include "core/cpu_manager.h"
18#include "core/hle/kernel/kernel.h" 21#include "core/hle/kernel/kernel.h"
22#include "core/hle/kernel/physical_core.h"
19#include "core/hle/kernel/process.h" 23#include "core/hle/kernel/process.h"
20#include "core/hle/kernel/scheduler.h" 24#include "core/hle/kernel/scheduler.h"
21#include "core/hle/kernel/time_manager.h" 25#include "core/hle/kernel/time_manager.h"
@@ -27,78 +31,108 @@ GlobalScheduler::GlobalScheduler(KernelCore& kernel) : kernel{kernel} {}
27GlobalScheduler::~GlobalScheduler() = default; 31GlobalScheduler::~GlobalScheduler() = default;
28 32
29void GlobalScheduler::AddThread(std::shared_ptr<Thread> thread) { 33void GlobalScheduler::AddThread(std::shared_ptr<Thread> thread) {
34 global_list_guard.lock();
30 thread_list.push_back(std::move(thread)); 35 thread_list.push_back(std::move(thread));
36 global_list_guard.unlock();
31} 37}
32 38
33void GlobalScheduler::RemoveThread(std::shared_ptr<Thread> thread) { 39void GlobalScheduler::RemoveThread(std::shared_ptr<Thread> thread) {
40 global_list_guard.lock();
34 thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread), 41 thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
35 thread_list.end()); 42 thread_list.end());
43 global_list_guard.unlock();
36} 44}
37 45
38void GlobalScheduler::UnloadThread(std::size_t core) { 46u32 GlobalScheduler::SelectThreads() {
39 Scheduler& sched = kernel.Scheduler(core);
40 sched.UnloadThread();
41}
42
43void GlobalScheduler::SelectThread(std::size_t core) {
44 const auto update_thread = [](Thread* thread, Scheduler& sched) { 47 const auto update_thread = [](Thread* thread, Scheduler& sched) {
48 sched.guard.lock();
45 if (thread != sched.selected_thread.get()) { 49 if (thread != sched.selected_thread.get()) {
46 if (thread == nullptr) { 50 if (thread == nullptr) {
47 ++sched.idle_selection_count; 51 ++sched.idle_selection_count;
48 } 52 }
49 sched.selected_thread = SharedFrom(thread); 53 sched.selected_thread = SharedFrom(thread);
50 } 54 }
51 sched.is_context_switch_pending = sched.selected_thread != sched.current_thread; 55 const bool reschedule_pending = sched.selected_thread != sched.current_thread;
56 sched.is_context_switch_pending = reschedule_pending;
52 std::atomic_thread_fence(std::memory_order_seq_cst); 57 std::atomic_thread_fence(std::memory_order_seq_cst);
58 sched.guard.unlock();
59 return reschedule_pending;
53 }; 60 };
54 Scheduler& sched = kernel.Scheduler(core); 61 if (!is_reselection_pending.load()) {
55 Thread* current_thread = nullptr; 62 return 0;
56 // Step 1: Get top thread in schedule queue.
57 current_thread = scheduled_queue[core].empty() ? nullptr : scheduled_queue[core].front();
58 if (current_thread) {
59 update_thread(current_thread, sched);
60 return;
61 } 63 }
62 // Step 2: Try selecting a suggested thread. 64 std::array<Thread*, Core::Hardware::NUM_CPU_CORES> top_threads{};
63 Thread* winner = nullptr; 65
64 std::set<s32> sug_cores; 66 u32 idle_cores{};
65 for (auto thread : suggested_queue[core]) { 67
66 s32 this_core = thread->GetProcessorID(); 68 // Step 1: Get top thread in schedule queue.
67 Thread* thread_on_core = nullptr; 69 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
68 if (this_core >= 0) { 70 Thread* top_thread =
69 thread_on_core = scheduled_queue[this_core].front(); 71 scheduled_queue[core].empty() ? nullptr : scheduled_queue[core].front();
70 } 72 if (top_thread != nullptr) {
71 if (this_core < 0 || thread != thread_on_core) { 73 // TODO(Blinkhawk): Implement Thread Pinning
72 winner = thread; 74 } else {
73 break; 75 idle_cores |= (1ul << core);
74 } 76 }
75 sug_cores.insert(this_core); 77 top_threads[core] = top_thread;
76 } 78 }
77 // if we got a suggested thread, select it, else do a second pass. 79
78 if (winner && winner->GetPriority() > 2) { 80 while (idle_cores != 0) {
79 if (winner->IsRunning()) { 81 u32 core_id = Common::CountTrailingZeroes32(idle_cores);
80 UnloadThread(static_cast<u32>(winner->GetProcessorID())); 82
83 if (!suggested_queue[core_id].empty()) {
84 std::array<s32, Core::Hardware::NUM_CPU_CORES> migration_candidates{};
85 std::size_t num_candidates = 0;
86 auto iter = suggested_queue[core_id].begin();
87 Thread* suggested = nullptr;
88 // Step 2: Try selecting a suggested thread.
89 while (iter != suggested_queue[core_id].end()) {
90 suggested = *iter;
91 iter++;
92 s32 suggested_core_id = suggested->GetProcessorID();
93 Thread* top_thread =
94 suggested_core_id > 0 ? top_threads[suggested_core_id] : nullptr;
95 if (top_thread != suggested) {
96 if (top_thread != nullptr &&
97 top_thread->GetPriority() < THREADPRIO_MAX_CORE_MIGRATION) {
98 suggested = nullptr;
99 break;
100 // There's a too high thread to do core migration, cancel
101 }
102 TransferToCore(suggested->GetPriority(), static_cast<s32>(core_id), suggested);
103 break;
104 }
105 migration_candidates[num_candidates++] = suggested_core_id;
106 }
107 // Step 3: Select a suggested thread from another core
108 if (suggested == nullptr) {
109 for (std::size_t i = 0; i < num_candidates; i++) {
110 s32 candidate_core = migration_candidates[i];
111 suggested = top_threads[candidate_core];
112 auto it = scheduled_queue[candidate_core].begin();
113 it++;
114 Thread* next = it != scheduled_queue[candidate_core].end() ? *it : nullptr;
115 if (next != nullptr) {
116 TransferToCore(suggested->GetPriority(), static_cast<s32>(core_id),
117 suggested);
118 top_threads[candidate_core] = next;
119 break;
120 }
121 }
122 }
123 top_threads[core_id] = suggested;
81 } 124 }
82 TransferToCore(winner->GetPriority(), static_cast<s32>(core), winner); 125
83 update_thread(winner, sched); 126 idle_cores &= ~(1ul << core_id);
84 return;
85 } 127 }
86 // Step 3: Select a suggested thread from another core 128 u32 cores_needing_context_switch{};
87 for (auto& src_core : sug_cores) { 129 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
88 auto it = scheduled_queue[src_core].begin(); 130 Scheduler& sched = kernel.Scheduler(core);
89 it++; 131 if (update_thread(top_threads[core], sched)) {
90 if (it != scheduled_queue[src_core].end()) { 132 cores_needing_context_switch |= (1ul << core);
91 Thread* thread_on_core = scheduled_queue[src_core].front();
92 Thread* to_change = *it;
93 if (thread_on_core->IsRunning() || to_change->IsRunning()) {
94 UnloadThread(static_cast<u32>(src_core));
95 }
96 TransferToCore(thread_on_core->GetPriority(), static_cast<s32>(core), thread_on_core);
97 current_thread = thread_on_core;
98 break;
99 } 133 }
100 } 134 }
101 update_thread(current_thread, sched); 135 return cores_needing_context_switch;
102} 136}
103 137
104bool GlobalScheduler::YieldThread(Thread* yielding_thread) { 138bool GlobalScheduler::YieldThread(Thread* yielding_thread) {
@@ -153,9 +187,6 @@ bool GlobalScheduler::YieldThreadAndBalanceLoad(Thread* yielding_thread) {
153 187
154 if (winner != nullptr) { 188 if (winner != nullptr) {
155 if (winner != yielding_thread) { 189 if (winner != yielding_thread) {
156 if (winner->IsRunning()) {
157 UnloadThread(static_cast<u32>(winner->GetProcessorID()));
158 }
159 TransferToCore(winner->GetPriority(), s32(core_id), winner); 190 TransferToCore(winner->GetPriority(), s32(core_id), winner);
160 } 191 }
161 } else { 192 } else {
@@ -195,9 +226,6 @@ bool GlobalScheduler::YieldThreadAndWaitForLoadBalancing(Thread* yielding_thread
195 } 226 }
196 if (winner != nullptr) { 227 if (winner != nullptr) {
197 if (winner != yielding_thread) { 228 if (winner != yielding_thread) {
198 if (winner->IsRunning()) {
199 UnloadThread(static_cast<u32>(winner->GetProcessorID()));
200 }
201 TransferToCore(winner->GetPriority(), static_cast<s32>(core_id), winner); 229 TransferToCore(winner->GetPriority(), static_cast<s32>(core_id), winner);
202 } 230 }
203 } else { 231 } else {
@@ -213,7 +241,9 @@ void GlobalScheduler::PreemptThreads() {
213 const u32 priority = preemption_priorities[core_id]; 241 const u32 priority = preemption_priorities[core_id];
214 242
215 if (scheduled_queue[core_id].size(priority) > 0) { 243 if (scheduled_queue[core_id].size(priority) > 0) {
216 scheduled_queue[core_id].front(priority)->IncrementYieldCount(); 244 if (scheduled_queue[core_id].size(priority) > 1) {
245 scheduled_queue[core_id].front(priority)->IncrementYieldCount();
246 }
217 scheduled_queue[core_id].yield(priority); 247 scheduled_queue[core_id].yield(priority);
218 if (scheduled_queue[core_id].size(priority) > 1) { 248 if (scheduled_queue[core_id].size(priority) > 1) {
219 scheduled_queue[core_id].front(priority)->IncrementYieldCount(); 249 scheduled_queue[core_id].front(priority)->IncrementYieldCount();
@@ -247,9 +277,6 @@ void GlobalScheduler::PreemptThreads() {
247 } 277 }
248 278
249 if (winner != nullptr) { 279 if (winner != nullptr) {
250 if (winner->IsRunning()) {
251 UnloadThread(static_cast<u32>(winner->GetProcessorID()));
252 }
253 TransferToCore(winner->GetPriority(), s32(core_id), winner); 280 TransferToCore(winner->GetPriority(), s32(core_id), winner);
254 current_thread = 281 current_thread =
255 winner->GetPriority() <= current_thread->GetPriority() ? winner : current_thread; 282 winner->GetPriority() <= current_thread->GetPriority() ? winner : current_thread;
@@ -280,9 +307,6 @@ void GlobalScheduler::PreemptThreads() {
280 } 307 }
281 308
282 if (winner != nullptr) { 309 if (winner != nullptr) {
283 if (winner->IsRunning()) {
284 UnloadThread(static_cast<u32>(winner->GetProcessorID()));
285 }
286 TransferToCore(winner->GetPriority(), s32(core_id), winner); 310 TransferToCore(winner->GetPriority(), s32(core_id), winner);
287 current_thread = winner; 311 current_thread = winner;
288 } 312 }
@@ -292,6 +316,28 @@ void GlobalScheduler::PreemptThreads() {
292 } 316 }
293} 317}
294 318
319void GlobalScheduler::EnableInterruptAndSchedule(u32 cores_pending_reschedule,
320 Core::EmuThreadHandle global_thread) {
321 u32 current_core = global_thread.host_handle;
322 bool must_context_switch = global_thread.guest_handle != InvalidHandle &&
323 (current_core < Core::Hardware::NUM_CPU_CORES);
324 while (cores_pending_reschedule != 0) {
325 u32 core = Common::CountTrailingZeroes32(cores_pending_reschedule);
326 ASSERT(core < Core::Hardware::NUM_CPU_CORES);
327 if (!must_context_switch || core != current_core) {
328 auto& phys_core = kernel.PhysicalCore(core);
329 phys_core.Interrupt();
330 } else {
331 must_context_switch = true;
332 }
333 cores_pending_reschedule &= ~(1ul << core);
334 }
335 if (must_context_switch) {
336 auto& core_scheduler = kernel.CurrentScheduler();
337 core_scheduler.TryDoContextSwitch();
338 }
339}
340
295void GlobalScheduler::Suggest(u32 priority, std::size_t core, Thread* thread) { 341void GlobalScheduler::Suggest(u32 priority, std::size_t core, Thread* thread) {
296 suggested_queue[core].add(thread, priority); 342 suggested_queue[core].add(thread, priority);
297} 343}
@@ -349,6 +395,108 @@ bool GlobalScheduler::AskForReselectionOrMarkRedundant(Thread* current_thread,
349 } 395 }
350} 396}
351 397
398void GlobalScheduler::AdjustSchedulingOnStatus(Thread* thread, u32 old_flags) {
399 if (old_flags == thread->scheduling_state) {
400 return;
401 }
402
403 if (static_cast<ThreadSchedStatus>(old_flags & static_cast<u32>(ThreadSchedMasks::LowMask)) ==
404 ThreadSchedStatus::Runnable) {
405 // In this case the thread was running, now it's pausing/exitting
406 if (thread->processor_id >= 0) {
407 Unschedule(thread->current_priority, static_cast<u32>(thread->processor_id), thread);
408 }
409
410 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
411 if (core != static_cast<u32>(thread->processor_id) &&
412 ((thread->affinity_mask >> core) & 1) != 0) {
413 Unsuggest(thread->current_priority, core, thread);
414 }
415 }
416 } else if (thread->GetSchedulingStatus() == ThreadSchedStatus::Runnable) {
417 // The thread is now set to running from being stopped
418 if (thread->processor_id >= 0) {
419 Schedule(thread->current_priority, static_cast<u32>(thread->processor_id), thread);
420 }
421
422 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
423 if (core != static_cast<u32>(thread->processor_id) &&
424 ((thread->affinity_mask >> core) & 1) != 0) {
425 Suggest(thread->current_priority, core, thread);
426 }
427 }
428 }
429
430 SetReselectionPending();
431}
432
433void GlobalScheduler::AdjustSchedulingOnPriority(Thread* thread, u32 old_priority) {
434 if (thread->GetSchedulingStatus() != ThreadSchedStatus::Runnable) {
435 return;
436 }
437 if (thread->processor_id >= 0) {
438 Unschedule(old_priority, static_cast<u32>(thread->processor_id), thread);
439 }
440
441 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
442 if (core != static_cast<u32>(thread->processor_id) &&
443 ((thread->affinity_mask >> core) & 1) != 0) {
444 Unsuggest(old_priority, core, thread);
445 }
446 }
447
448 if (thread->processor_id >= 0) {
449 // TODO(Blinkhawk): compare it with current thread running on current core, instead of
450 // checking running
451 if (thread->IsRunning()) {
452 SchedulePrepend(thread->current_priority, static_cast<u32>(thread->processor_id),
453 thread);
454 } else {
455 Schedule(thread->current_priority, static_cast<u32>(thread->processor_id), thread);
456 }
457 }
458
459 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
460 if (core != static_cast<u32>(thread->processor_id) &&
461 ((thread->affinity_mask >> core) & 1) != 0) {
462 Suggest(thread->current_priority, core, thread);
463 }
464 }
465 thread->IncrementYieldCount();
466 SetReselectionPending();
467}
468
469void GlobalScheduler::AdjustSchedulingOnAffinity(Thread* thread, u64 old_affinity_mask,
470 s32 old_core) {
471 if (thread->GetSchedulingStatus() != ThreadSchedStatus::Runnable ||
472 thread->current_priority >= THREADPRIO_COUNT) {
473 return;
474 }
475
476 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
477 if (((old_affinity_mask >> core) & 1) != 0) {
478 if (core == static_cast<u32>(old_core)) {
479 Unschedule(thread->current_priority, core, thread);
480 } else {
481 Unsuggest(thread->current_priority, core, thread);
482 }
483 }
484 }
485
486 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
487 if (((thread->affinity_mask >> core) & 1) != 0) {
488 if (core == static_cast<u32>(thread->processor_id)) {
489 Schedule(thread->current_priority, core, thread);
490 } else {
491 Suggest(thread->current_priority, core, thread);
492 }
493 }
494 }
495
496 thread->IncrementYieldCount();
497 SetReselectionPending();
498}
499
352void GlobalScheduler::Shutdown() { 500void GlobalScheduler::Shutdown() {
353 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) { 501 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
354 scheduled_queue[core].clear(); 502 scheduled_queue[core].clear();
@@ -374,13 +522,12 @@ void GlobalScheduler::Unlock() {
374 ASSERT(scope_lock > 0); 522 ASSERT(scope_lock > 0);
375 return; 523 return;
376 } 524 }
377 for (std::size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; i++) { 525 u32 cores_pending_reschedule = SelectThreads();
378 SelectThread(i); 526 Core::EmuThreadHandle leaving_thread = current_owner;
379 }
380 current_owner = Core::EmuThreadHandle::InvalidHandle(); 527 current_owner = Core::EmuThreadHandle::InvalidHandle();
381 scope_lock = 1; 528 scope_lock = 1;
382 inner_lock.unlock(); 529 inner_lock.unlock();
383 // TODO(Blinkhawk): Setup the interrupts and change context on current core. 530 EnableInterruptAndSchedule(cores_pending_reschedule, leaving_thread);
384} 531}
385 532
386Scheduler::Scheduler(Core::System& system, std::size_t core_id) 533Scheduler::Scheduler(Core::System& system, std::size_t core_id)
@@ -393,56 +540,83 @@ bool Scheduler::HaveReadyThreads() const {
393} 540}
394 541
395Thread* Scheduler::GetCurrentThread() const { 542Thread* Scheduler::GetCurrentThread() const {
396 return current_thread.get(); 543 if (current_thread) {
544 return current_thread.get();
545 }
546 return idle_thread.get();
397} 547}
398 548
399Thread* Scheduler::GetSelectedThread() const { 549Thread* Scheduler::GetSelectedThread() const {
400 return selected_thread.get(); 550 return selected_thread.get();
401} 551}
402 552
403void Scheduler::SelectThreads() {
404 system.GlobalScheduler().SelectThread(core_id);
405}
406
407u64 Scheduler::GetLastContextSwitchTicks() const { 553u64 Scheduler::GetLastContextSwitchTicks() const {
408 return last_context_switch_time; 554 return last_context_switch_time;
409} 555}
410 556
411void Scheduler::TryDoContextSwitch() { 557void Scheduler::TryDoContextSwitch() {
558 auto& phys_core = system.Kernel().CurrentPhysicalCore();
559 if (phys_core.IsInterrupted()) {
560 phys_core.ClearInterrupt();
561 }
562 guard.lock();
412 if (is_context_switch_pending) { 563 if (is_context_switch_pending) {
413 SwitchContext(); 564 SwitchContext();
565 } else {
566 guard.unlock();
414 } 567 }
415} 568}
416 569
417void Scheduler::UnloadThread() { 570void Scheduler::OnThreadStart() {
418 Thread* const previous_thread = GetCurrentThread(); 571 SwitchContextStep2();
419 Process* const previous_process = system.Kernel().CurrentProcess(); 572}
420 573
421 UpdateLastContextSwitchTime(previous_thread, previous_process); 574void Scheduler::SwitchContextStep2() {
575 Thread* previous_thread = current_thread.get();
576 Thread* new_thread = selected_thread.get();
422 577
423 // Save context for previous thread 578 // Load context of new thread
424 if (previous_thread) { 579 Process* const previous_process =
425 system.ArmInterface(core_id).SaveContext(previous_thread->GetContext32()); 580 previous_thread != nullptr ? previous_thread->GetOwnerProcess() : nullptr;
426 system.ArmInterface(core_id).SaveContext(previous_thread->GetContext64());
427 // Save the TPIDR_EL0 system register in case it was modified.
428 previous_thread->SetTPIDR_EL0(system.ArmInterface(core_id).GetTPIDR_EL0());
429 581
430 if (previous_thread->GetStatus() == ThreadStatus::Running) { 582 if (new_thread) {
431 // This is only the case when a reschedule is triggered without the current thread 583 new_thread->context_guard.lock();
432 // yielding execution (i.e. an event triggered, system core time-sliced, etc) 584 ASSERT_MSG(new_thread->GetProcessorID() == s32(this->core_id),
433 previous_thread->SetStatus(ThreadStatus::Ready); 585 "Thread must be assigned to this core.");
586 ASSERT_MSG(new_thread->GetStatus() == ThreadStatus::Ready,
587 "Thread must be ready to become running.");
588
589 // Cancel any outstanding wakeup events for this thread
590 current_thread = SharedFrom(new_thread);
591 new_thread->SetStatus(ThreadStatus::Running);
592 new_thread->SetIsRunning(true);
593
594 auto* const thread_owner_process = current_thread->GetOwnerProcess();
595 if (previous_process != thread_owner_process && thread_owner_process != nullptr) {
596 system.Kernel().MakeCurrentProcess(thread_owner_process);
434 } 597 }
435 previous_thread->SetIsRunning(false); 598 if (!new_thread->IsHLEThread()) {
599 auto& cpu_core = system.ArmInterface(core_id);
600 cpu_core.LoadContext(new_thread->GetContext32());
601 cpu_core.LoadContext(new_thread->GetContext64());
602 cpu_core.SetTlsAddress(new_thread->GetTLSAddress());
603 cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0());
604 }
605 } else {
606 current_thread = nullptr;
607 // Note: We do not reset the current process and current page table when idling because
608 // technically we haven't changed processes, our threads are just paused.
436 } 609 }
437 current_thread = nullptr; 610 guard.unlock();
438} 611}
439 612
440void Scheduler::SwitchContext() { 613void Scheduler::SwitchContext() {
441 Thread* const previous_thread = GetCurrentThread(); 614 Thread* previous_thread = current_thread.get();
442 Thread* const new_thread = GetSelectedThread(); 615 Thread* new_thread = selected_thread.get();
443 616
444 is_context_switch_pending = false; 617 is_context_switch_pending = false;
445 if (new_thread == previous_thread) { 618 if (new_thread == previous_thread) {
619 guard.unlock();
446 return; 620 return;
447 } 621 }
448 622
@@ -452,51 +626,44 @@ void Scheduler::SwitchContext() {
452 626
453 // Save context for previous thread 627 // Save context for previous thread
454 if (previous_thread) { 628 if (previous_thread) {
455 system.ArmInterface(core_id).SaveContext(previous_thread->GetContext32()); 629 if (!previous_thread->IsHLEThread()) {
456 system.ArmInterface(core_id).SaveContext(previous_thread->GetContext64()); 630 auto& cpu_core = system.ArmInterface(core_id);
457 // Save the TPIDR_EL0 system register in case it was modified. 631 cpu_core.SaveContext(previous_thread->GetContext32());
458 previous_thread->SetTPIDR_EL0(system.ArmInterface(core_id).GetTPIDR_EL0()); 632 cpu_core.SaveContext(previous_thread->GetContext64());
633 // Save the TPIDR_EL0 system register in case it was modified.
634 previous_thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
459 635
636 }
460 if (previous_thread->GetStatus() == ThreadStatus::Running) { 637 if (previous_thread->GetStatus() == ThreadStatus::Running) {
461 // This is only the case when a reschedule is triggered without the current thread
462 // yielding execution (i.e. an event triggered, system core time-sliced, etc)
463 previous_thread->SetStatus(ThreadStatus::Ready); 638 previous_thread->SetStatus(ThreadStatus::Ready);
464 } 639 }
465 previous_thread->SetIsRunning(false); 640 previous_thread->SetIsRunning(false);
641 previous_thread->context_guard.unlock();
466 } 642 }
467 643
468 // Load context of new thread 644 std::shared_ptr<Common::Fiber> old_context;
469 if (new_thread) { 645 if (previous_thread != nullptr) {
470 ASSERT_MSG(new_thread->GetProcessorID() == s32(this->core_id), 646 old_context = previous_thread->GetHostContext();
471 "Thread must be assigned to this core."); 647 } else {
472 ASSERT_MSG(new_thread->GetStatus() == ThreadStatus::Ready, 648 old_context = idle_thread->GetHostContext();
473 "Thread must be ready to become running."); 649 }
474
475 // Cancel any outstanding wakeup events for this thread
476 new_thread->CancelWakeupTimer();
477 current_thread = SharedFrom(new_thread);
478 new_thread->SetStatus(ThreadStatus::Running);
479 new_thread->SetIsRunning(true);
480
481 auto* const thread_owner_process = current_thread->GetOwnerProcess();
482 if (previous_process != thread_owner_process) {
483 system.Kernel().MakeCurrentProcess(thread_owner_process);
484 }
485 650
486 system.ArmInterface(core_id).LoadContext(new_thread->GetContext32()); 651 std::shared_ptr<Common::Fiber> next_context;
487 system.ArmInterface(core_id).LoadContext(new_thread->GetContext64()); 652 if (new_thread != nullptr) {
488 system.ArmInterface(core_id).SetTlsAddress(new_thread->GetTLSAddress()); 653 next_context = new_thread->GetHostContext();
489 system.ArmInterface(core_id).SetTPIDR_EL0(new_thread->GetTPIDR_EL0());
490 } else { 654 } else {
491 current_thread = nullptr; 655 next_context = idle_thread->GetHostContext();
492 // Note: We do not reset the current process and current page table when idling because
493 // technically we haven't changed processes, our threads are just paused.
494 } 656 }
657
658 Common::Fiber::YieldTo(old_context, next_context);
659 /// When a thread wakes up, the scheduler may have changed to other in another core.
660 auto& next_scheduler = system.Kernel().CurrentScheduler();
661 next_scheduler.SwitchContextStep2();
495} 662}
496 663
497void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) { 664void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
498 const u64 prev_switch_ticks = last_context_switch_time; 665 const u64 prev_switch_ticks = last_context_switch_time;
499 const u64 most_recent_switch_ticks = system.CoreTiming().GetTicks(); 666 const u64 most_recent_switch_ticks = system.CoreTiming().GetCPUTicks();
500 const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks; 667 const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
501 668
502 if (thread != nullptr) { 669 if (thread != nullptr) {
@@ -510,6 +677,16 @@ void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
510 last_context_switch_time = most_recent_switch_ticks; 677 last_context_switch_time = most_recent_switch_ticks;
511} 678}
512 679
680void Scheduler::Initialize() {
681 std::string name = "Idle Thread Id:" + std::to_string(core_id);
682 std::function<void(void*)> init_func = system.GetCpuManager().GetIdleThreadStartFunc();
683 void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
684 ThreadType type = static_cast<ThreadType>(THREADTYPE_KERNEL | THREADTYPE_HLE | THREADTYPE_IDLE);
685 auto thread_res = Thread::Create(system, type, name, 0, 64, 0, static_cast<u32>(core_id), 0,
686 nullptr, std::move(init_func), init_func_parameter);
687 idle_thread = std::move(thread_res).Unwrap();
688}
689
513void Scheduler::Shutdown() { 690void Scheduler::Shutdown() {
514 current_thread = nullptr; 691 current_thread = nullptr;
515 selected_thread = nullptr; 692 selected_thread = nullptr;
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h
index 07df33f9c..16655b03f 100644
--- a/src/core/hle/kernel/scheduler.h
+++ b/src/core/hle/kernel/scheduler.h
@@ -11,6 +11,7 @@
11 11
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "common/multi_level_queue.h" 13#include "common/multi_level_queue.h"
14#include "common/spin_lock.h"
14#include "core/hardware_properties.h" 15#include "core/hardware_properties.h"
15#include "core/hle/kernel/thread.h" 16#include "core/hle/kernel/thread.h"
16 17
@@ -41,41 +42,17 @@ public:
41 return thread_list; 42 return thread_list;
42 } 43 }
43 44
44 /** 45 /// Notify the scheduler a thread's status has changed.
45 * Add a thread to the suggested queue of a cpu core. Suggested threads may be 46 void AdjustSchedulingOnStatus(Thread* thread, u32 old_flags);
46 * picked if no thread is scheduled to run on the core.
47 */
48 void Suggest(u32 priority, std::size_t core, Thread* thread);
49
50 /**
51 * Remove a thread to the suggested queue of a cpu core. Suggested threads may be
52 * picked if no thread is scheduled to run on the core.
53 */
54 void Unsuggest(u32 priority, std::size_t core, Thread* thread);
55
56 /**
57 * Add a thread to the scheduling queue of a cpu core. The thread is added at the
58 * back the queue in its priority level.
59 */
60 void Schedule(u32 priority, std::size_t core, Thread* thread);
61 47
62 /** 48 /// Notify the scheduler a thread's priority has changed.
63 * Add a thread to the scheduling queue of a cpu core. The thread is added at the 49 void AdjustSchedulingOnPriority(Thread* thread, u32 old_priority);
64 * front the queue in its priority level.
65 */
66 void SchedulePrepend(u32 priority, std::size_t core, Thread* thread);
67 50
68 /// Reschedule an already scheduled thread based on a new priority 51 /// Notify the scheduler a thread's core and/or affinity mask has changed.
69 void Reschedule(u32 priority, std::size_t core, Thread* thread); 52 void AdjustSchedulingOnAffinity(Thread* thread, u64 old_affinity_mask, s32 old_core);
70
71 /// Unschedules a thread.
72 void Unschedule(u32 priority, std::size_t core, Thread* thread);
73
74 /// Selects a core and forces it to unload its current thread's context
75 void UnloadThread(std::size_t core);
76 53
77 /** 54 /**
78 * Takes care of selecting the new scheduled thread in three steps: 55 * Takes care of selecting the new scheduled threads in three steps:
79 * 56 *
80 * 1. First a thread is selected from the top of the priority queue. If no thread 57 * 1. First a thread is selected from the top of the priority queue. If no thread
81 * is obtained then we move to step two, else we are done. 58 * is obtained then we move to step two, else we are done.
@@ -85,8 +62,10 @@ public:
85 * 62 *
86 * 3. Third is no suggested thread is found, we do a second pass and pick a running 63 * 3. Third is no suggested thread is found, we do a second pass and pick a running
87 * thread in another core and swap it with its current thread. 64 * thread in another core and swap it with its current thread.
65 *
66 * returns the cores needing scheduling.
88 */ 67 */
89 void SelectThread(std::size_t core); 68 u32 SelectThreads();
90 69
91 bool HaveReadyThreads(std::size_t core_id) const { 70 bool HaveReadyThreads(std::size_t core_id) const {
92 return !scheduled_queue[core_id].empty(); 71 return !scheduled_queue[core_id].empty();
@@ -149,6 +128,39 @@ private:
149 /// Unlocks the scheduler, reselects threads, interrupts cores for rescheduling 128 /// Unlocks the scheduler, reselects threads, interrupts cores for rescheduling
150 /// and reschedules current core if needed. 129 /// and reschedules current core if needed.
151 void Unlock(); 130 void Unlock();
131
132 void EnableInterruptAndSchedule(u32 cores_pending_reschedule, Core::EmuThreadHandle global_thread);
133
134 /**
135 * Add a thread to the suggested queue of a cpu core. Suggested threads may be
136 * picked if no thread is scheduled to run on the core.
137 */
138 void Suggest(u32 priority, std::size_t core, Thread* thread);
139
140 /**
141 * Remove a thread to the suggested queue of a cpu core. Suggested threads may be
142 * picked if no thread is scheduled to run on the core.
143 */
144 void Unsuggest(u32 priority, std::size_t core, Thread* thread);
145
146 /**
147 * Add a thread to the scheduling queue of a cpu core. The thread is added at the
148 * back the queue in its priority level.
149 */
150 void Schedule(u32 priority, std::size_t core, Thread* thread);
151
152 /**
153 * Add a thread to the scheduling queue of a cpu core. The thread is added at the
154 * front the queue in its priority level.
155 */
156 void SchedulePrepend(u32 priority, std::size_t core, Thread* thread);
157
158 /// Reschedule an already scheduled thread based on a new priority
159 void Reschedule(u32 priority, std::size_t core, Thread* thread);
160
161 /// Unschedules a thread.
162 void Unschedule(u32 priority, std::size_t core, Thread* thread);
163
152 /** 164 /**
153 * Transfers a thread into an specific core. If the destination_core is -1 165 * Transfers a thread into an specific core. If the destination_core is -1
154 * it will be unscheduled from its source code and added into its suggested 166 * it will be unscheduled from its source code and added into its suggested
@@ -174,6 +186,8 @@ private:
174 std::atomic<s64> scope_lock{}; 186 std::atomic<s64> scope_lock{};
175 Core::EmuThreadHandle current_owner{Core::EmuThreadHandle::InvalidHandle()}; 187 Core::EmuThreadHandle current_owner{Core::EmuThreadHandle::InvalidHandle()};
176 188
189 Common::SpinLock global_list_guard{};
190
177 /// Lists all thread ids that aren't deleted/etc. 191 /// Lists all thread ids that aren't deleted/etc.
178 std::vector<std::shared_ptr<Thread>> thread_list; 192 std::vector<std::shared_ptr<Thread>> thread_list;
179 KernelCore& kernel; 193 KernelCore& kernel;
@@ -190,12 +204,6 @@ public:
190 /// Reschedules to the next available thread (call after current thread is suspended) 204 /// Reschedules to the next available thread (call after current thread is suspended)
191 void TryDoContextSwitch(); 205 void TryDoContextSwitch();
192 206
193 /// Unloads currently running thread
194 void UnloadThread();
195
196 /// Select the threads in top of the scheduling multilist.
197 void SelectThreads();
198
199 /// Gets the current running thread 207 /// Gets the current running thread
200 Thread* GetCurrentThread() const; 208 Thread* GetCurrentThread() const;
201 209
@@ -209,15 +217,22 @@ public:
209 return is_context_switch_pending; 217 return is_context_switch_pending;
210 } 218 }
211 219
220 void Initialize();
221
212 /// Shutdowns the scheduler. 222 /// Shutdowns the scheduler.
213 void Shutdown(); 223 void Shutdown();
214 224
225 void OnThreadStart();
226
215private: 227private:
216 friend class GlobalScheduler; 228 friend class GlobalScheduler;
217 229
218 /// Switches the CPU's active thread context to that of the specified thread 230 /// Switches the CPU's active thread context to that of the specified thread
219 void SwitchContext(); 231 void SwitchContext();
220 232
233 /// When a thread wakes up, it must run this through it's new scheduler
234 void SwitchContextStep2();
235
221 /** 236 /**
222 * Called on every context switch to update the internal timestamp 237 * Called on every context switch to update the internal timestamp
223 * This also updates the running time ticks for the given thread and 238 * This also updates the running time ticks for the given thread and
@@ -233,12 +248,15 @@ private:
233 248
234 std::shared_ptr<Thread> current_thread = nullptr; 249 std::shared_ptr<Thread> current_thread = nullptr;
235 std::shared_ptr<Thread> selected_thread = nullptr; 250 std::shared_ptr<Thread> selected_thread = nullptr;
251 std::shared_ptr<Thread> idle_thread = nullptr;
236 252
237 Core::System& system; 253 Core::System& system;
238 u64 last_context_switch_time = 0; 254 u64 last_context_switch_time = 0;
239 u64 idle_selection_count = 0; 255 u64 idle_selection_count = 0;
240 const std::size_t core_id; 256 const std::size_t core_id;
241 257
258 Common::SpinLock guard{};
259
242 bool is_context_switch_pending = false; 260 bool is_context_switch_pending = false;
243}; 261};
244 262
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 4ae4529f5..d7f0dcabd 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -863,9 +863,9 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
863 if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) { 863 if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
864 const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks(); 864 const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks();
865 865
866 out_ticks = thread_ticks + (core_timing.GetTicks() - prev_ctx_ticks); 866 out_ticks = thread_ticks + (core_timing.GetCPUTicks() - prev_ctx_ticks);
867 } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) { 867 } else if (same_thread && info_sub_id == system.CurrentCoreIndex()) {
868 out_ticks = core_timing.GetTicks() - prev_ctx_ticks; 868 out_ticks = core_timing.GetCPUTicks() - prev_ctx_ticks;
869 } 869 }
870 870
871 *result = out_ticks; 871 *result = out_ticks;
@@ -1428,9 +1428,10 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e
1428 1428
1429 ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(ResourceType::Threads, 1)); 1429 ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(ResourceType::Threads, 1));
1430 1430
1431 ThreadType type = THREADTYPE_USER;
1431 CASCADE_RESULT(std::shared_ptr<Thread> thread, 1432 CASCADE_RESULT(std::shared_ptr<Thread> thread,
1432 Thread::Create(kernel, "", entry_point, priority, arg, processor_id, stack_top, 1433 Thread::Create(system, type, "", entry_point, priority, arg, processor_id, stack_top,
1433 *current_process)); 1434 current_process));
1434 1435
1435 const auto new_thread_handle = current_process->GetHandleTable().Create(thread); 1436 const auto new_thread_handle = current_process->GetHandleTable().Create(thread);
1436 if (new_thread_handle.Failed()) { 1437 if (new_thread_handle.Failed()) {
@@ -1513,13 +1514,6 @@ static void SleepThread(Core::System& system, s64 nanoseconds) {
1513 } else { 1514 } else {
1514 current_thread->Sleep(nanoseconds); 1515 current_thread->Sleep(nanoseconds);
1515 } 1516 }
1516
1517 if (is_redundant) {
1518 // If it's redundant, the core is pretty much idle. Some games keep idling
1519 // a core while it's doing nothing, we advance timing to avoid costly continuous
1520 // calls.
1521 system.CoreTiming().AddTicks(2000);
1522 }
1523 system.PrepareReschedule(current_thread->GetProcessorID()); 1517 system.PrepareReschedule(current_thread->GetProcessorID());
1524} 1518}
1525 1519
@@ -1725,10 +1719,7 @@ static u64 GetSystemTick(Core::System& system) {
1725 auto& core_timing = system.CoreTiming(); 1719 auto& core_timing = system.CoreTiming();
1726 1720
1727 // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick) 1721 // Returns the value of cntpct_el0 (https://switchbrew.org/wiki/SVC#svcGetSystemTick)
1728 const u64 result{Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks())}; 1722 const u64 result{system.CoreTiming().GetClockTicks()};
1729
1730 // Advance time to defeat dumb games that busy-wait for the frame to end.
1731 core_timing.AddTicks(400);
1732 1723
1733 return result; 1724 return result;
1734} 1725}
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index db7f379ac..8cb3593db 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -9,12 +9,14 @@
9 9
10#include "common/assert.h" 10#include "common/assert.h"
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/fiber.h"
12#include "common/logging/log.h" 13#include "common/logging/log.h"
13#include "common/thread_queue_list.h" 14#include "common/thread_queue_list.h"
14#include "core/arm/arm_interface.h" 15#include "core/arm/arm_interface.h"
15#include "core/core.h" 16#include "core/core.h"
16#include "core/core_timing.h" 17#include "core/core_timing.h"
17#include "core/core_timing_util.h" 18#include "core/core_timing_util.h"
19#include "core/cpu_manager.h"
18#include "core/hardware_properties.h" 20#include "core/hardware_properties.h"
19#include "core/hle/kernel/errors.h" 21#include "core/hle/kernel/errors.h"
20#include "core/hle/kernel/handle_table.h" 22#include "core/hle/kernel/handle_table.h"
@@ -23,6 +25,7 @@
23#include "core/hle/kernel/process.h" 25#include "core/hle/kernel/process.h"
24#include "core/hle/kernel/scheduler.h" 26#include "core/hle/kernel/scheduler.h"
25#include "core/hle/kernel/thread.h" 27#include "core/hle/kernel/thread.h"
28#include "core/hle/kernel/time_manager.h"
26#include "core/hle/result.h" 29#include "core/hle/result.h"
27#include "core/memory.h" 30#include "core/memory.h"
28 31
@@ -44,6 +47,7 @@ Thread::Thread(KernelCore& kernel) : SynchronizationObject{kernel} {}
44Thread::~Thread() = default; 47Thread::~Thread() = default;
45 48
46void Thread::Stop() { 49void Thread::Stop() {
50 SchedulerLock lock(kernel);
47 // Cancel any outstanding wakeup events for this thread 51 // Cancel any outstanding wakeup events for this thread
48 Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(), 52 Core::System::GetInstance().CoreTiming().UnscheduleEvent(kernel.ThreadWakeupCallbackEventType(),
49 global_handle); 53 global_handle);
@@ -71,9 +75,8 @@ void Thread::WakeAfterDelay(s64 nanoseconds) {
71 75
72 // This function might be called from any thread so we have to be cautious and use the 76 // This function might be called from any thread so we have to be cautious and use the
73 // thread-safe version of ScheduleEvent. 77 // thread-safe version of ScheduleEvent.
74 const s64 cycles = Core::Timing::nsToCycles(std::chrono::nanoseconds{nanoseconds});
75 Core::System::GetInstance().CoreTiming().ScheduleEvent( 78 Core::System::GetInstance().CoreTiming().ScheduleEvent(
76 cycles, kernel.ThreadWakeupCallbackEventType(), global_handle); 79 nanoseconds, kernel.ThreadWakeupCallbackEventType(), global_handle);
77} 80}
78 81
79void Thread::CancelWakeupTimer() { 82void Thread::CancelWakeupTimer() {
@@ -125,6 +128,16 @@ void Thread::ResumeFromWait() {
125 SetStatus(ThreadStatus::Ready); 128 SetStatus(ThreadStatus::Ready);
126} 129}
127 130
131void Thread::OnWakeUp() {
132 SchedulerLock lock(kernel);
133 if (activity == ThreadActivity::Paused) {
134 SetStatus(ThreadStatus::Paused);
135 return;
136 }
137
138 SetStatus(ThreadStatus::Ready);
139}
140
128void Thread::CancelWait() { 141void Thread::CancelWait() {
129 if (GetSchedulingStatus() != ThreadSchedStatus::Paused) { 142 if (GetSchedulingStatus() != ThreadSchedStatus::Paused) {
130 is_sync_cancelled = true; 143 is_sync_cancelled = true;
@@ -153,12 +166,29 @@ static void ResetThreadContext64(Core::ARM_Interface::ThreadContext64& context,
153 context.fpcr = 0; 166 context.fpcr = 0;
154} 167}
155 168
156ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::string name, 169std::shared_ptr<Common::Fiber> Thread::GetHostContext() const {
157 VAddr entry_point, u32 priority, u64 arg, 170 return host_context;
158 s32 processor_id, VAddr stack_top, 171}
159 Process& owner_process) { 172
173ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadType type_flags,
174 std::string name, VAddr entry_point, u32 priority,
175 u64 arg, s32 processor_id, VAddr stack_top,
176 Process* owner_process) {
177 std::function<void(void*)> init_func = system.GetCpuManager().GetGuestThreadStartFunc();
178 void* init_func_parameter = system.GetCpuManager().GetStartFuncParamater();
179 return Create(system, type_flags, name, entry_point, priority, arg, processor_id, stack_top,
180 owner_process, std::move(init_func), init_func_parameter);
181}
182
183ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadType type_flags,
184 std::string name, VAddr entry_point, u32 priority,
185 u64 arg, s32 processor_id, VAddr stack_top,
186 Process* owner_process,
187 std::function<void(void*)>&& thread_start_func,
188 void* thread_start_parameter) {
189 auto& kernel = system.Kernel();
160 // Check if priority is in ranged. Lowest priority -> highest priority id. 190 // Check if priority is in ranged. Lowest priority -> highest priority id.
161 if (priority > THREADPRIO_LOWEST) { 191 if (priority > THREADPRIO_LOWEST && (type_flags & THREADTYPE_IDLE == 0)) {
162 LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority); 192 LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority);
163 return ERR_INVALID_THREAD_PRIORITY; 193 return ERR_INVALID_THREAD_PRIORITY;
164 } 194 }
@@ -168,11 +198,12 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::strin
168 return ERR_INVALID_PROCESSOR_ID; 198 return ERR_INVALID_PROCESSOR_ID;
169 } 199 }
170 200
171 auto& system = Core::System::GetInstance(); 201 if (owner_process) {
172 if (!system.Memory().IsValidVirtualAddress(owner_process, entry_point)) { 202 if (!system.Memory().IsValidVirtualAddress(*owner_process, entry_point)) {
173 LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point); 203 LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point);
174 // TODO (bunnei): Find the correct error code to use here 204 // TODO (bunnei): Find the correct error code to use here
175 return RESULT_UNKNOWN; 205 return RESULT_UNKNOWN;
206 }
176 } 207 }
177 208
178 std::shared_ptr<Thread> thread = std::make_shared<Thread>(kernel); 209 std::shared_ptr<Thread> thread = std::make_shared<Thread>(kernel);
@@ -183,7 +214,7 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::strin
183 thread->stack_top = stack_top; 214 thread->stack_top = stack_top;
184 thread->tpidr_el0 = 0; 215 thread->tpidr_el0 = 0;
185 thread->nominal_priority = thread->current_priority = priority; 216 thread->nominal_priority = thread->current_priority = priority;
186 thread->last_running_ticks = system.CoreTiming().GetTicks(); 217 thread->last_running_ticks = 0;
187 thread->processor_id = processor_id; 218 thread->processor_id = processor_id;
188 thread->ideal_core = processor_id; 219 thread->ideal_core = processor_id;
189 thread->affinity_mask = 1ULL << processor_id; 220 thread->affinity_mask = 1ULL << processor_id;
@@ -193,16 +224,27 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(KernelCore& kernel, std::strin
193 thread->wait_handle = 0; 224 thread->wait_handle = 0;
194 thread->name = std::move(name); 225 thread->name = std::move(name);
195 thread->global_handle = kernel.GlobalHandleTable().Create(thread).Unwrap(); 226 thread->global_handle = kernel.GlobalHandleTable().Create(thread).Unwrap();
196 thread->owner_process = &owner_process; 227 thread->owner_process = owner_process;
197 auto& scheduler = kernel.GlobalScheduler(); 228 thread->type = type_flags;
198 scheduler.AddThread(thread); 229 if ((type_flags & THREADTYPE_IDLE) == 0) {
199 thread->tls_address = thread->owner_process->CreateTLSRegion(); 230 auto& scheduler = kernel.GlobalScheduler();
200 231 scheduler.AddThread(thread);
201 thread->owner_process->RegisterThread(thread.get()); 232 }
202 233 if (owner_process) {
203 ResetThreadContext32(thread->context_32, static_cast<u32>(stack_top), 234 thread->tls_address = thread->owner_process->CreateTLSRegion();
204 static_cast<u32>(entry_point), static_cast<u32>(arg)); 235 thread->owner_process->RegisterThread(thread.get());
205 ResetThreadContext64(thread->context_64, stack_top, entry_point, arg); 236 } else {
237 thread->tls_address = 0;
238 }
239 // TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used
240 // to initialize the context
241 if ((type_flags & THREADTYPE_HLE) == 0) {
242 ResetThreadContext32(thread->context_32, static_cast<u32>(stack_top),
243 static_cast<u32>(entry_point), static_cast<u32>(arg));
244 ResetThreadContext64(thread->context_64, stack_top, entry_point, arg);
245 }
246 thread->host_context =
247 std::make_shared<Common::Fiber>(std::move(thread_start_func), thread_start_parameter);
206 248
207 return MakeResult<std::shared_ptr<Thread>>(std::move(thread)); 249 return MakeResult<std::shared_ptr<Thread>>(std::move(thread));
208} 250}
@@ -258,7 +300,7 @@ void Thread::SetStatus(ThreadStatus new_status) {
258 } 300 }
259 301
260 if (status == ThreadStatus::Running) { 302 if (status == ThreadStatus::Running) {
261 last_running_ticks = Core::System::GetInstance().CoreTiming().GetTicks(); 303 last_running_ticks = Core::System::GetInstance().CoreTiming().GetCPUTicks();
262 } 304 }
263 305
264 status = new_status; 306 status = new_status;
@@ -375,38 +417,55 @@ void Thread::SetActivity(ThreadActivity value) {
375} 417}
376 418
377void Thread::Sleep(s64 nanoseconds) { 419void Thread::Sleep(s64 nanoseconds) {
378 // Sleep current thread and check for next thread to schedule 420 Handle event_handle{};
379 SetStatus(ThreadStatus::WaitSleep); 421 {
422 SchedulerLockAndSleep lock(kernel, event_handle, this, nanoseconds);
423 SetStatus(ThreadStatus::WaitSleep);
424 }
380 425
381 // Create an event to wake the thread up after the specified nanosecond delay has passed 426 if (event_handle != InvalidHandle) {
382 WakeAfterDelay(nanoseconds); 427 auto& time_manager = kernel.TimeManager();
428 time_manager.UnscheduleTimeEvent(event_handle);
429 }
383} 430}
384 431
385bool Thread::YieldSimple() { 432bool Thread::YieldSimple() {
386 auto& scheduler = kernel.GlobalScheduler(); 433 bool result{};
387 return scheduler.YieldThread(this); 434 {
435 SchedulerLock lock(kernel);
436 result = kernel.GlobalScheduler().YieldThread(this);
437 }
438 return result;
388} 439}
389 440
390bool Thread::YieldAndBalanceLoad() { 441bool Thread::YieldAndBalanceLoad() {
391 auto& scheduler = kernel.GlobalScheduler(); 442 bool result{};
392 return scheduler.YieldThreadAndBalanceLoad(this); 443 {
444 SchedulerLock lock(kernel);
445 result = kernel.GlobalScheduler().YieldThreadAndBalanceLoad(this);
446 }
447 return result;
393} 448}
394 449
395bool Thread::YieldAndWaitForLoadBalancing() { 450bool Thread::YieldAndWaitForLoadBalancing() {
396 auto& scheduler = kernel.GlobalScheduler(); 451 bool result{};
397 return scheduler.YieldThreadAndWaitForLoadBalancing(this); 452 {
453 SchedulerLock lock(kernel);
454 result = kernel.GlobalScheduler().YieldThreadAndWaitForLoadBalancing(this);
455 }
456 return result;
398} 457}
399 458
400void Thread::SetSchedulingStatus(ThreadSchedStatus new_status) { 459void Thread::SetSchedulingStatus(ThreadSchedStatus new_status) {
401 const u32 old_flags = scheduling_state; 460 const u32 old_flags = scheduling_state;
402 scheduling_state = (scheduling_state & static_cast<u32>(ThreadSchedMasks::HighMask)) | 461 scheduling_state = (scheduling_state & static_cast<u32>(ThreadSchedMasks::HighMask)) |
403 static_cast<u32>(new_status); 462 static_cast<u32>(new_status);
404 AdjustSchedulingOnStatus(old_flags); 463 kernel.GlobalScheduler().AdjustSchedulingOnStatus(this, old_flags);
405} 464}
406 465
407void Thread::SetCurrentPriority(u32 new_priority) { 466void Thread::SetCurrentPriority(u32 new_priority) {
408 const u32 old_priority = std::exchange(current_priority, new_priority); 467 const u32 old_priority = std::exchange(current_priority, new_priority);
409 AdjustSchedulingOnPriority(old_priority); 468 kernel.GlobalScheduler().AdjustSchedulingOnPriority(this, old_priority);
410} 469}
411 470
412ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) { 471ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) {
@@ -443,111 +502,12 @@ ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) {
443 processor_id = ideal_core; 502 processor_id = ideal_core;
444 } 503 }
445 } 504 }
446 AdjustSchedulingOnAffinity(old_affinity_mask, old_core); 505 kernel.GlobalScheduler().AdjustSchedulingOnAffinity(this, old_affinity_mask, old_core);
447 } 506 }
448 } 507 }
449 return RESULT_SUCCESS; 508 return RESULT_SUCCESS;
450} 509}
451 510
452void Thread::AdjustSchedulingOnStatus(u32 old_flags) {
453 if (old_flags == scheduling_state) {
454 return;
455 }
456
457 auto& scheduler = kernel.GlobalScheduler();
458 if (static_cast<ThreadSchedStatus>(old_flags & static_cast<u32>(ThreadSchedMasks::LowMask)) ==
459 ThreadSchedStatus::Runnable) {
460 // In this case the thread was running, now it's pausing/exitting
461 if (processor_id >= 0) {
462 scheduler.Unschedule(current_priority, static_cast<u32>(processor_id), this);
463 }
464
465 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
466 if (core != static_cast<u32>(processor_id) && ((affinity_mask >> core) & 1) != 0) {
467 scheduler.Unsuggest(current_priority, core, this);
468 }
469 }
470 } else if (GetSchedulingStatus() == ThreadSchedStatus::Runnable) {
471 // The thread is now set to running from being stopped
472 if (processor_id >= 0) {
473 scheduler.Schedule(current_priority, static_cast<u32>(processor_id), this);
474 }
475
476 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
477 if (core != static_cast<u32>(processor_id) && ((affinity_mask >> core) & 1) != 0) {
478 scheduler.Suggest(current_priority, core, this);
479 }
480 }
481 }
482
483 scheduler.SetReselectionPending();
484}
485
486void Thread::AdjustSchedulingOnPriority(u32 old_priority) {
487 if (GetSchedulingStatus() != ThreadSchedStatus::Runnable) {
488 return;
489 }
490 auto& scheduler = kernel.GlobalScheduler();
491 if (processor_id >= 0) {
492 scheduler.Unschedule(old_priority, static_cast<u32>(processor_id), this);
493 }
494
495 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
496 if (core != static_cast<u32>(processor_id) && ((affinity_mask >> core) & 1) != 0) {
497 scheduler.Unsuggest(old_priority, core, this);
498 }
499 }
500
501 // Add thread to the new priority queues.
502 Thread* current_thread = GetCurrentThread();
503
504 if (processor_id >= 0) {
505 if (current_thread == this) {
506 scheduler.SchedulePrepend(current_priority, static_cast<u32>(processor_id), this);
507 } else {
508 scheduler.Schedule(current_priority, static_cast<u32>(processor_id), this);
509 }
510 }
511
512 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
513 if (core != static_cast<u32>(processor_id) && ((affinity_mask >> core) & 1) != 0) {
514 scheduler.Suggest(current_priority, core, this);
515 }
516 }
517
518 scheduler.SetReselectionPending();
519}
520
521void Thread::AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core) {
522 auto& scheduler = kernel.GlobalScheduler();
523 if (GetSchedulingStatus() != ThreadSchedStatus::Runnable ||
524 current_priority >= THREADPRIO_COUNT) {
525 return;
526 }
527
528 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
529 if (((old_affinity_mask >> core) & 1) != 0) {
530 if (core == static_cast<u32>(old_core)) {
531 scheduler.Unschedule(current_priority, core, this);
532 } else {
533 scheduler.Unsuggest(current_priority, core, this);
534 }
535 }
536 }
537
538 for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
539 if (((affinity_mask >> core) & 1) != 0) {
540 if (core == static_cast<u32>(processor_id)) {
541 scheduler.Schedule(current_priority, core, this);
542 } else {
543 scheduler.Suggest(current_priority, core, this);
544 }
545 }
546 }
547
548 scheduler.SetReselectionPending();
549}
550
551//////////////////////////////////////////////////////////////////////////////////////////////////// 511////////////////////////////////////////////////////////////////////////////////////////////////////
552 512
553/** 513/**
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 23fdef8a4..33d340b47 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -9,23 +9,42 @@
9#include <vector> 9#include <vector>
10 10
11#include "common/common_types.h" 11#include "common/common_types.h"
12#include "common/spin_lock.h"
12#include "core/arm/arm_interface.h" 13#include "core/arm/arm_interface.h"
13#include "core/hle/kernel/object.h" 14#include "core/hle/kernel/object.h"
14#include "core/hle/kernel/synchronization_object.h" 15#include "core/hle/kernel/synchronization_object.h"
15#include "core/hle/result.h" 16#include "core/hle/result.h"
16 17
18namespace Common {
19class Fiber;
20}
21
22namespace Core {
23class System;
24}
25
17namespace Kernel { 26namespace Kernel {
18 27
28class GlobalScheduler;
19class KernelCore; 29class KernelCore;
20class Process; 30class Process;
21class Scheduler; 31class Scheduler;
22 32
23enum ThreadPriority : u32 { 33enum ThreadPriority : u32 {
24 THREADPRIO_HIGHEST = 0, ///< Highest thread priority 34 THREADPRIO_HIGHEST = 0, ///< Highest thread priority
25 THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps 35 THREADPRIO_MAX_CORE_MIGRATION = 2, ///< Highest priority for a core migration
26 THREADPRIO_DEFAULT = 44, ///< Default thread priority for userland apps 36 THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps
27 THREADPRIO_LOWEST = 63, ///< Lowest thread priority 37 THREADPRIO_DEFAULT = 44, ///< Default thread priority for userland apps
28 THREADPRIO_COUNT = 64, ///< Total number of possible thread priorities. 38 THREADPRIO_LOWEST = 63, ///< Lowest thread priority
39 THREADPRIO_COUNT = 64, ///< Total number of possible thread priorities.
40};
41
42enum ThreadType : u32 {
43 THREADTYPE_USER = 0x1,
44 THREADTYPE_KERNEL = 0x2,
45 THREADTYPE_HLE = 0x4,
46 THREADTYPE_IDLE = 0x8,
47 THREADTYPE_SUSPEND = 0x10,
29}; 48};
30 49
31enum ThreadProcessorId : s32 { 50enum ThreadProcessorId : s32 {
@@ -111,22 +130,43 @@ public:
111 std::function<bool(ThreadWakeupReason reason, std::shared_ptr<Thread> thread, 130 std::function<bool(ThreadWakeupReason reason, std::shared_ptr<Thread> thread,
112 std::shared_ptr<SynchronizationObject> object, std::size_t index)>; 131 std::shared_ptr<SynchronizationObject> object, std::size_t index)>;
113 132
133 /**
134 * Creates and returns a new thread. The new thread is immediately scheduled
135 * @param system The instance of the whole system
136 * @param name The friendly name desired for the thread
137 * @param entry_point The address at which the thread should start execution
138 * @param priority The thread's priority
139 * @param arg User data to pass to the thread
140 * @param processor_id The ID(s) of the processors on which the thread is desired to be run
141 * @param stack_top The address of the thread's stack top
142 * @param owner_process The parent process for the thread, if null, it's a kernel thread
143 * @return A shared pointer to the newly created thread
144 */
145 static ResultVal<std::shared_ptr<Thread>> Create(Core::System& system, ThreadType type_flags, std::string name,
146 VAddr entry_point, u32 priority, u64 arg,
147 s32 processor_id, VAddr stack_top,
148 Process* owner_process);
149
114 /** 150 /**
115 * Creates and returns a new thread. The new thread is immediately scheduled 151 * Creates and returns a new thread. The new thread is immediately scheduled
116 * @param kernel The kernel instance this thread will be created under. 152 * @param system The instance of the whole system
117 * @param name The friendly name desired for the thread 153 * @param name The friendly name desired for the thread
118 * @param entry_point The address at which the thread should start execution 154 * @param entry_point The address at which the thread should start execution
119 * @param priority The thread's priority 155 * @param priority The thread's priority
120 * @param arg User data to pass to the thread 156 * @param arg User data to pass to the thread
121 * @param processor_id The ID(s) of the processors on which the thread is desired to be run 157 * @param processor_id The ID(s) of the processors on which the thread is desired to be run
122 * @param stack_top The address of the thread's stack top 158 * @param stack_top The address of the thread's stack top
123 * @param owner_process The parent process for the thread 159 * @param owner_process The parent process for the thread, if null, it's a kernel thread
160 * @param thread_start_func The function where the host context will start.
161 * @param thread_start_parameter The parameter which will passed to host context on init
124 * @return A shared pointer to the newly created thread 162 * @return A shared pointer to the newly created thread
125 */ 163 */
126 static ResultVal<std::shared_ptr<Thread>> Create(KernelCore& kernel, std::string name, 164 static ResultVal<std::shared_ptr<Thread>> Create(Core::System& system, ThreadType type_flags, std::string name,
127 VAddr entry_point, u32 priority, u64 arg, 165 VAddr entry_point, u32 priority, u64 arg,
128 s32 processor_id, VAddr stack_top, 166 s32 processor_id, VAddr stack_top,
129 Process& owner_process); 167 Process* owner_process,
168 std::function<void(void*)>&& thread_start_func,
169 void* thread_start_parameter);
130 170
131 std::string GetName() const override { 171 std::string GetName() const override {
132 return name; 172 return name;
@@ -192,7 +232,9 @@ public:
192 } 232 }
193 233
194 /// Resumes a thread from waiting 234 /// Resumes a thread from waiting
195 void ResumeFromWait(); 235 void /* deprecated */ ResumeFromWait();
236
237 void OnWakeUp();
196 238
197 /// Cancels a waiting operation that this thread may or may not be within. 239 /// Cancels a waiting operation that this thread may or may not be within.
198 /// 240 ///
@@ -206,10 +248,10 @@ public:
206 * Schedules an event to wake up the specified thread after the specified delay 248 * Schedules an event to wake up the specified thread after the specified delay
207 * @param nanoseconds The time this thread will be allowed to sleep for 249 * @param nanoseconds The time this thread will be allowed to sleep for
208 */ 250 */
209 void WakeAfterDelay(s64 nanoseconds); 251 void /* deprecated */ WakeAfterDelay(s64 nanoseconds);
210 252
211 /// Cancel any outstanding wakeup events for this thread 253 /// Cancel any outstanding wakeup events for this thread
212 void CancelWakeupTimer(); 254 void /* deprecated */ CancelWakeupTimer();
213 255
214 /** 256 /**
215 * Sets the result after the thread awakens (from svcWaitSynchronization) 257 * Sets the result after the thread awakens (from svcWaitSynchronization)
@@ -290,6 +332,12 @@ public:
290 return context_64; 332 return context_64;
291 } 333 }
292 334
335 bool IsHLEThread() const {
336 return (type & THREADTYPE_HLE) != 0;
337 }
338
339 std::shared_ptr<Common::Fiber> GetHostContext() const;
340
293 ThreadStatus GetStatus() const { 341 ThreadStatus GetStatus() const {
294 return status; 342 return status;
295 } 343 }
@@ -467,16 +515,19 @@ public:
467 } 515 }
468 516
469private: 517private:
518 friend class GlobalScheduler;
519 friend class Scheduler;
520
470 void SetSchedulingStatus(ThreadSchedStatus new_status); 521 void SetSchedulingStatus(ThreadSchedStatus new_status);
471 void SetCurrentPriority(u32 new_priority); 522 void SetCurrentPriority(u32 new_priority);
472 ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask); 523 ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask);
473 524
474 void AdjustSchedulingOnStatus(u32 old_flags);
475 void AdjustSchedulingOnPriority(u32 old_priority);
476 void AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core); 525 void AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core);
477 526
478 ThreadContext32 context_32{}; 527 ThreadContext32 context_32{};
479 ThreadContext64 context_64{}; 528 ThreadContext64 context_64{};
529 Common::SpinLock context_guard{};
530 std::shared_ptr<Common::Fiber> host_context{};
480 531
481 u64 thread_id = 0; 532 u64 thread_id = 0;
482 533
@@ -485,6 +536,8 @@ private:
485 VAddr entry_point = 0; 536 VAddr entry_point = 0;
486 VAddr stack_top = 0; 537 VAddr stack_top = 0;
487 538
539 ThreadType type;
540
488 /// Nominal thread priority, as set by the emulated application. 541 /// Nominal thread priority, as set by the emulated application.
489 /// The nominal priority is the thread priority without priority 542 /// The nominal priority is the thread priority without priority
490 /// inheritance taken into account. 543 /// inheritance taken into account.
diff --git a/src/core/hle/kernel/time_manager.cpp b/src/core/hle/kernel/time_manager.cpp
index 21b290468..0b8f0d993 100644
--- a/src/core/hle/kernel/time_manager.cpp
+++ b/src/core/hle/kernel/time_manager.cpp
@@ -19,7 +19,7 @@ TimeManager::TimeManager(Core::System& system) : system{system} {
19 Handle proper_handle = static_cast<Handle>(thread_handle); 19 Handle proper_handle = static_cast<Handle>(thread_handle);
20 std::shared_ptr<Thread> thread = 20 std::shared_ptr<Thread> thread =
21 this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle); 21 this->system.Kernel().RetrieveThreadFromGlobalHandleTable(proper_handle);
22 thread->ResumeFromWait(); 22 thread->OnWakeUp();
23 }); 23 });
24} 24}
25 25