summaryrefslogtreecommitdiff
path: root/src
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
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')
-rw-r--r--src/common/thread.cpp6
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/arm/arm_interface.h5
-rw-r--r--src/core/arm/cpu_interrupt_handler.cpp29
-rw-r--r--src/core/arm/cpu_interrupt_handler.h39
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp6
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.h4
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp28
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.h4
-rw-r--r--src/core/arm/unicorn/arm_unicorn.cpp14
-rw-r--r--src/core/arm/unicorn/arm_unicorn.h3
-rw-r--r--src/core/core.cpp57
-rw-r--r--src/core/core.h34
-rw-r--r--src/core/core_manager.cpp4
-rw-r--r--src/core/core_timing.cpp208
-rw-r--r--src/core/core_timing.h108
-rw-r--r--src/core/cpu_manager.cpp194
-rw-r--r--src/core/cpu_manager.h49
-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
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/mouse.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp4
-rw-r--r--src/core/hle/service/hid/controllers/xpad.cpp2
-rw-r--r--src/core/hle/service/hid/hid.cpp16
-rw-r--r--src/core/hle/service/hid/irs.cpp2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp3
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp13
-rw-r--r--src/core/hle/service/time/standard_steady_clock_core.cpp5
-rw-r--r--src/core/hle/service/time/tick_based_steady_clock_core.cpp5
-rw-r--r--src/core/hle/service/time/time.cpp5
-rw-r--r--src/core/hle/service/time/time_sharedmemory.cpp3
-rw-r--r--src/core/memory.cpp11
-rw-r--r--src/core/memory.h2
-rw-r--r--src/core/memory/cheat_engine.cpp8
-rw-r--r--src/core/tools/freezer.cpp8
-rw-r--r--src/tests/CMakeLists.txt1
-rw-r--r--src/tests/core/core_timing.cpp184
-rw-r--r--src/video_core/gpu.cpp5
-rw-r--r--src/yuzu/bootmanager.cpp32
-rw-r--r--src/yuzu/bootmanager.h7
-rw-r--r--src/yuzu/debugger/wait_tree.cpp6
-rw-r--r--src/yuzu_cmd/yuzu.cpp2
-rw-r--r--src/yuzu_tester/yuzu.cpp2
57 files changed, 1341 insertions, 816 deletions
diff --git a/src/common/thread.cpp b/src/common/thread.cpp
index 0cd2d10bf..c9684aed9 100644
--- a/src/common/thread.cpp
+++ b/src/common/thread.cpp
@@ -70,6 +70,12 @@ void SetCurrentThreadName(const char* name) {
70} 70}
71#endif 71#endif
72 72
73#if defined(_WIN32)
74void SetCurrentThreadName(const char* name) {
75 // Do Nothing on MingW
76}
77#endif
78
73#endif 79#endif
74 80
75} // namespace Common 81} // namespace Common
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index efbad628f..552094ddb 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -7,6 +7,8 @@ endif()
7add_library(core STATIC 7add_library(core STATIC
8 arm/arm_interface.h 8 arm/arm_interface.h
9 arm/arm_interface.cpp 9 arm/arm_interface.cpp
10 arm/cpu_interrupt_handler.cpp
11 arm/cpu_interrupt_handler.h
10 arm/exclusive_monitor.cpp 12 arm/exclusive_monitor.cpp
11 arm/exclusive_monitor.h 13 arm/exclusive_monitor.h
12 arm/unicorn/arm_unicorn.cpp 14 arm/unicorn/arm_unicorn.cpp
@@ -547,8 +549,6 @@ add_library(core STATIC
547 hle/service/vi/vi_u.h 549 hle/service/vi/vi_u.h
548 hle/service/wlan/wlan.cpp 550 hle/service/wlan/wlan.cpp
549 hle/service/wlan/wlan.h 551 hle/service/wlan/wlan.h
550 host_timing.cpp
551 host_timing.h
552 loader/deconstructed_rom_directory.cpp 552 loader/deconstructed_rom_directory.cpp
553 loader/deconstructed_rom_directory.h 553 loader/deconstructed_rom_directory.h
554 loader/elf.cpp 554 loader/elf.cpp
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index cb2e640e2..87a1c29cc 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -18,11 +18,13 @@ enum class VMAPermission : u8;
18 18
19namespace Core { 19namespace Core {
20class System; 20class System;
21class CPUInterruptHandler;
21 22
22/// Generic ARMv8 CPU interface 23/// Generic ARMv8 CPU interface
23class ARM_Interface : NonCopyable { 24class ARM_Interface : NonCopyable {
24public: 25public:
25 explicit ARM_Interface(System& system_) : system{system_} {} 26 explicit ARM_Interface(System& system_, CPUInterruptHandler& interrupt_handler)
27 : system{system_}, interrupt_handler{interrupt_handler} {}
26 virtual ~ARM_Interface() = default; 28 virtual ~ARM_Interface() = default;
27 29
28 struct ThreadContext32 { 30 struct ThreadContext32 {
@@ -175,6 +177,7 @@ public:
175protected: 177protected:
176 /// System context that this ARM interface is running under. 178 /// System context that this ARM interface is running under.
177 System& system; 179 System& system;
180 CPUInterruptHandler& interrupt_handler;
178}; 181};
179 182
180} // namespace Core 183} // namespace Core
diff --git a/src/core/arm/cpu_interrupt_handler.cpp b/src/core/arm/cpu_interrupt_handler.cpp
new file mode 100644
index 000000000..2f1a1a269
--- /dev/null
+++ b/src/core/arm/cpu_interrupt_handler.cpp
@@ -0,0 +1,29 @@
1// Copyright 2020 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/thread.h"
8#include "core/arm/cpu_interrupt_handler.h"
9
10namespace Core {
11
12CPUInterruptHandler::CPUInterruptHandler() : is_interrupted{} {
13 interrupt_event = std::make_unique<Common::Event>();
14}
15
16CPUInterruptHandler::~CPUInterruptHandler() = default;
17
18void CPUInterruptHandler::SetInterrupt(bool is_interrupted_) {
19 if (is_interrupted_) {
20 interrupt_event->Set();
21 }
22 this->is_interrupted = is_interrupted_;
23}
24
25void CPUInterruptHandler::AwaitInterrupt() {
26 interrupt_event->Wait();
27}
28
29} // namespace Core
diff --git a/src/core/arm/cpu_interrupt_handler.h b/src/core/arm/cpu_interrupt_handler.h
new file mode 100644
index 000000000..91c31a271
--- /dev/null
+++ b/src/core/arm/cpu_interrupt_handler.h
@@ -0,0 +1,39 @@
1// Copyright 2020 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8
9namespace Common {
10class Event;
11}
12
13namespace Core {
14
15class CPUInterruptHandler {
16public:
17 CPUInterruptHandler();
18 ~CPUInterruptHandler();
19
20 CPUInterruptHandler(const CPUInterruptHandler&) = delete;
21 CPUInterruptHandler& operator=(const CPUInterruptHandler&) = delete;
22
23 CPUInterruptHandler(CPUInterruptHandler&&) = default;
24 CPUInterruptHandler& operator=(CPUInterruptHandler&&) = default;
25
26 constexpr bool IsInterrupted() const {
27 return is_interrupted;
28 }
29
30 void SetInterrupt(bool is_interrupted);
31
32 void AwaitInterrupt();
33
34private:
35 bool is_interrupted{};
36 std::unique_ptr<Common::Event> interrupt_event;
37};
38
39} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index 4c8663d03..0b7aa6a69 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -114,9 +114,9 @@ void ARM_Dynarmic_32::Step() {
114 jit->Step(); 114 jit->Step();
115} 115}
116 116
117ARM_Dynarmic_32::ARM_Dynarmic_32(System& system, ExclusiveMonitor& exclusive_monitor, 117ARM_Dynarmic_32::ARM_Dynarmic_32(System& system, CPUInterruptHandler& interrupt_handler,
118 std::size_t core_index) 118 ExclusiveMonitor& exclusive_monitor, std::size_t core_index)
119 : ARM_Interface{system}, cb(std::make_unique<DynarmicCallbacks32>(*this)), 119 : ARM_Interface{system, interrupt_handler}, cb(std::make_unique<DynarmicCallbacks32>(*this)),
120 cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index}, 120 cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index},
121 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {} 121 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
122 122
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h
index e5b92d7bb..1e7e17e64 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.h
@@ -21,6 +21,7 @@ class Memory;
21 21
22namespace Core { 22namespace Core {
23 23
24class CPUInterruptHandler;
24class DynarmicCallbacks32; 25class DynarmicCallbacks32;
25class DynarmicCP15; 26class DynarmicCP15;
26class DynarmicExclusiveMonitor; 27class DynarmicExclusiveMonitor;
@@ -28,7 +29,8 @@ class System;
28 29
29class ARM_Dynarmic_32 final : public ARM_Interface { 30class ARM_Dynarmic_32 final : public ARM_Interface {
30public: 31public:
31 ARM_Dynarmic_32(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index); 32 ARM_Dynarmic_32(System& system, CPUInterruptHandler& interrupt_handler,
33 ExclusiveMonitor& exclusive_monitor, std::size_t core_index);
32 ~ARM_Dynarmic_32() override; 34 ~ARM_Dynarmic_32() override;
33 35
34 void SetPC(u64 pc) override; 36 void SetPC(u64 pc) override;
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index 5f5e36d94..5e316ffd4 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -9,6 +9,7 @@
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/microprofile.h" 10#include "common/microprofile.h"
11#include "common/page_table.h" 11#include "common/page_table.h"
12#include "core/arm/cpu_interrupt_handler.h"
12#include "core/arm/dynarmic/arm_dynarmic_64.h" 13#include "core/arm/dynarmic/arm_dynarmic_64.h"
13#include "core/core.h" 14#include "core/core.h"
14#include "core/core_manager.h" 15#include "core/core_manager.h"
@@ -108,23 +109,16 @@ public:
108 } 109 }
109 110
110 void AddTicks(u64 ticks) override { 111 void AddTicks(u64 ticks) override {
111 // Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a 112 /// We are using host timing, NOP
112 // rough approximation of the amount of executed ticks in the system, it may be thrown off
113 // if not all cores are doing a similar amount of work. Instead of doing this, we should
114 // device a way so that timing is consistent across all cores without increasing the ticks 4
115 // times.
116 u64 amortized_ticks = (ticks - num_interpreted_instructions) / Core::NUM_CPU_CORES;
117 // Always execute at least one tick.
118 amortized_ticks = std::max<u64>(amortized_ticks, 1);
119
120 parent.system.CoreTiming().AddTicks(amortized_ticks);
121 num_interpreted_instructions = 0;
122 } 113 }
123 u64 GetTicksRemaining() override { 114 u64 GetTicksRemaining() override {
124 return std::max(parent.system.CoreTiming().GetDowncount(), s64{0}); 115 if (!parent.interrupt_handler.IsInterrupted()) {
116 return 1000ULL;
117 }
118 return 0ULL;
125 } 119 }
126 u64 GetCNTPCT() override { 120 u64 GetCNTPCT() override {
127 return Timing::CpuCyclesToClockCycles(parent.system.CoreTiming().GetTicks()); 121 return parent.system.CoreTiming().GetClockTicks();
128 } 122 }
129 123
130 ARM_Dynarmic_64& parent; 124 ARM_Dynarmic_64& parent;
@@ -183,10 +177,10 @@ void ARM_Dynarmic_64::Step() {
183 cb->InterpreterFallback(jit->GetPC(), 1); 177 cb->InterpreterFallback(jit->GetPC(), 1);
184} 178}
185 179
186ARM_Dynarmic_64::ARM_Dynarmic_64(System& system, ExclusiveMonitor& exclusive_monitor, 180ARM_Dynarmic_64::ARM_Dynarmic_64(System& system, CPUInterruptHandler& interrupt_handler,
187 std::size_t core_index) 181 ExclusiveMonitor& exclusive_monitor, std::size_t core_index)
188 : ARM_Interface{system}, cb(std::make_unique<DynarmicCallbacks64>(*this)), 182 : ARM_Interface{system, interrupt_handler}, cb(std::make_unique<DynarmicCallbacks64>(*this)),
189 inner_unicorn{system, ARM_Unicorn::Arch::AArch64}, core_index{core_index}, 183 inner_unicorn{system, interrupt_handler, ARM_Unicorn::Arch::AArch64}, core_index{core_index},
190 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {} 184 exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
191 185
192ARM_Dynarmic_64::~ARM_Dynarmic_64() = default; 186ARM_Dynarmic_64::~ARM_Dynarmic_64() = default;
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h
index 647cecaf0..9e94b58c2 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.h
@@ -22,12 +22,14 @@ class Memory;
22namespace Core { 22namespace Core {
23 23
24class DynarmicCallbacks64; 24class DynarmicCallbacks64;
25class CPUInterruptHandler;
25class DynarmicExclusiveMonitor; 26class DynarmicExclusiveMonitor;
26class System; 27class System;
27 28
28class ARM_Dynarmic_64 final : public ARM_Interface { 29class ARM_Dynarmic_64 final : public ARM_Interface {
29public: 30public:
30 ARM_Dynarmic_64(System& system, ExclusiveMonitor& exclusive_monitor, std::size_t core_index); 31 ARM_Dynarmic_64(System& system, CPUInterruptHandler& interrupt_handler,
32 ExclusiveMonitor& exclusive_monitor, std::size_t core_index);
31 ~ARM_Dynarmic_64() override; 33 ~ARM_Dynarmic_64() override;
32 34
33 void SetPC(u64 pc) override; 35 void SetPC(u64 pc) override;
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp
index e40e9626a..0393fe641 100644
--- a/src/core/arm/unicorn/arm_unicorn.cpp
+++ b/src/core/arm/unicorn/arm_unicorn.cpp
@@ -6,6 +6,7 @@
6#include <unicorn/arm64.h> 6#include <unicorn/arm64.h>
7#include "common/assert.h" 7#include "common/assert.h"
8#include "common/microprofile.h" 8#include "common/microprofile.h"
9#include "core/arm/cpu_interrupt_handler.h"
9#include "core/arm/unicorn/arm_unicorn.h" 10#include "core/arm/unicorn/arm_unicorn.h"
10#include "core/core.h" 11#include "core/core.h"
11#include "core/core_timing.h" 12#include "core/core_timing.h"
@@ -62,7 +63,8 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si
62 return false; 63 return false;
63} 64}
64 65
65ARM_Unicorn::ARM_Unicorn(System& system, Arch architecture) : ARM_Interface{system} { 66ARM_Unicorn::ARM_Unicorn(System& system, CPUInterruptHandler& interrupt_handler, Arch architecture)
67 : ARM_Interface{system, interrupt_handler} {
66 const auto arch = architecture == Arch::AArch32 ? UC_ARCH_ARM : UC_ARCH_ARM64; 68 const auto arch = architecture == Arch::AArch32 ? UC_ARCH_ARM : UC_ARCH_ARM64;
67 CHECKED(uc_open(arch, UC_MODE_ARM, &uc)); 69 CHECKED(uc_open(arch, UC_MODE_ARM, &uc));
68 70
@@ -160,8 +162,12 @@ void ARM_Unicorn::Run() {
160 if (GDBStub::IsServerEnabled()) { 162 if (GDBStub::IsServerEnabled()) {
161 ExecuteInstructions(std::max(4000000U, 0U)); 163 ExecuteInstructions(std::max(4000000U, 0U));
162 } else { 164 } else {
163 ExecuteInstructions( 165 while (true) {
164 std::max(std::size_t(system.CoreTiming().GetDowncount()), std::size_t{0})); 166 if (interrupt_handler.IsInterrupted()) {
167 return;
168 }
169 ExecuteInstructions(10);
170 }
165 } 171 }
166} 172}
167 173
@@ -183,8 +189,6 @@ void ARM_Unicorn::ExecuteInstructions(std::size_t num_instructions) {
183 UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC, page_buffer.data())); 189 UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC, page_buffer.data()));
184 CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions)); 190 CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions));
185 CHECKED(uc_mem_unmap(uc, map_addr, page_buffer.size())); 191 CHECKED(uc_mem_unmap(uc, map_addr, page_buffer.size()));
186
187 system.CoreTiming().AddTicks(num_instructions);
188 if (GDBStub::IsServerEnabled()) { 192 if (GDBStub::IsServerEnabled()) {
189 if (last_bkpt_hit && last_bkpt.type == GDBStub::BreakpointType::Execute) { 193 if (last_bkpt_hit && last_bkpt.type == GDBStub::BreakpointType::Execute) {
190 uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address); 194 uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address);
diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h
index 725c65085..0a4c087cd 100644
--- a/src/core/arm/unicorn/arm_unicorn.h
+++ b/src/core/arm/unicorn/arm_unicorn.h
@@ -11,6 +11,7 @@
11 11
12namespace Core { 12namespace Core {
13 13
14class CPUInterruptHandler;
14class System; 15class System;
15 16
16class ARM_Unicorn final : public ARM_Interface { 17class ARM_Unicorn final : public ARM_Interface {
@@ -20,7 +21,7 @@ public:
20 AArch64, // 64-bit ARM 21 AArch64, // 64-bit ARM
21 }; 22 };
22 23
23 explicit ARM_Unicorn(System& system, Arch architecture); 24 explicit ARM_Unicorn(System& system, CPUInterruptHandler& interrupt_handler, Arch architecture);
24 ~ARM_Unicorn() override; 25 ~ARM_Unicorn() override;
25 26
26 void SetPC(u64 pc) override; 27 void SetPC(u64 pc) override;
diff --git a/src/core/core.cpp b/src/core/core.cpp
index f9f8a3000..e8936b09d 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -11,7 +11,6 @@
11#include "common/string_util.h" 11#include "common/string_util.h"
12#include "core/arm/exclusive_monitor.h" 12#include "core/arm/exclusive_monitor.h"
13#include "core/core.h" 13#include "core/core.h"
14#include "core/core_manager.h"
15#include "core/core_timing.h" 14#include "core/core_timing.h"
16#include "core/cpu_manager.h" 15#include "core/cpu_manager.h"
17#include "core/device_memory.h" 16#include "core/device_memory.h"
@@ -117,23 +116,30 @@ struct System::Impl {
117 : kernel{system}, fs_controller{system}, memory{system}, 116 : kernel{system}, fs_controller{system}, memory{system},
118 cpu_manager{system}, reporter{system}, applet_manager{system} {} 117 cpu_manager{system}, reporter{system}, applet_manager{system} {}
119 118
120 CoreManager& CurrentCoreManager() {
121 return cpu_manager.GetCurrentCoreManager();
122 }
123
124 Kernel::PhysicalCore& CurrentPhysicalCore() { 119 Kernel::PhysicalCore& CurrentPhysicalCore() {
125 const auto index = cpu_manager.GetActiveCoreIndex(); 120 return kernel.CurrentPhysicalCore();
126 return kernel.PhysicalCore(index);
127 } 121 }
128 122
129 Kernel::PhysicalCore& GetPhysicalCore(std::size_t index) { 123 Kernel::PhysicalCore& GetPhysicalCore(std::size_t index) {
130 return kernel.PhysicalCore(index); 124 return kernel.PhysicalCore(index);
131 } 125 }
132 126
133 ResultStatus RunLoop(bool tight_loop) { 127 ResultStatus Run() {
134 status = ResultStatus::Success; 128 status = ResultStatus::Success;
135 129
136 cpu_manager.RunLoop(tight_loop); 130 kernel.Suspend(false);
131 core_timing.SyncPause(false);
132 cpu_manager.Pause(false);
133
134 return status;
135 }
136
137 ResultStatus Pause() {
138 status = ResultStatus::Success;
139
140 kernel.Suspend(true);
141 core_timing.SyncPause(true);
142 cpu_manager.Pause(true);
137 143
138 return status; 144 return status;
139 } 145 }
@@ -143,7 +149,7 @@ struct System::Impl {
143 149
144 device_memory = std::make_unique<Core::DeviceMemory>(system); 150 device_memory = std::make_unique<Core::DeviceMemory>(system);
145 151
146 core_timing.Initialize(); 152 core_timing.Initialize([&system]() { system.RegisterHostThread(); });
147 kernel.Initialize(); 153 kernel.Initialize();
148 cpu_manager.Initialize(); 154 cpu_manager.Initialize();
149 155
@@ -387,20 +393,24 @@ struct System::Impl {
387System::System() : impl{std::make_unique<Impl>(*this)} {} 393System::System() : impl{std::make_unique<Impl>(*this)} {}
388System::~System() = default; 394System::~System() = default;
389 395
390CoreManager& System::CurrentCoreManager() { 396CpuManager& System::GetCpuManager() {
391 return impl->CurrentCoreManager(); 397 return impl->cpu_manager;
398}
399
400const CpuManager& System::GetCpuManager() const {
401 return impl->cpu_manager;
392} 402}
393 403
394const CoreManager& System::CurrentCoreManager() const { 404System::ResultStatus System::Run() {
395 return impl->CurrentCoreManager(); 405 return impl->Run();
396} 406}
397 407
398System::ResultStatus System::RunLoop(bool tight_loop) { 408System::ResultStatus System::Pause() {
399 return impl->RunLoop(tight_loop); 409 return impl->Pause();
400} 410}
401 411
402System::ResultStatus System::SingleStep() { 412System::ResultStatus System::SingleStep() {
403 return RunLoop(false); 413 return ResultStatus::Success;
404} 414}
405 415
406void System::InvalidateCpuInstructionCaches() { 416void System::InvalidateCpuInstructionCaches() {
@@ -444,7 +454,9 @@ const ARM_Interface& System::CurrentArmInterface() const {
444} 454}
445 455
446std::size_t System::CurrentCoreIndex() const { 456std::size_t System::CurrentCoreIndex() const {
447 return impl->cpu_manager.GetActiveCoreIndex(); 457 std::size_t core = impl->kernel.GetCurrentHostThreadID();
458 ASSERT(core < Core::Hardware::NUM_CPU_CORES);
459 return core;
448} 460}
449 461
450Kernel::Scheduler& System::CurrentScheduler() { 462Kernel::Scheduler& System::CurrentScheduler() {
@@ -497,15 +509,6 @@ const ARM_Interface& System::ArmInterface(std::size_t core_index) const {
497 return impl->GetPhysicalCore(core_index).ArmInterface(); 509 return impl->GetPhysicalCore(core_index).ArmInterface();
498} 510}
499 511
500CoreManager& System::GetCoreManager(std::size_t core_index) {
501 return impl->cpu_manager.GetCoreManager(core_index);
502}
503
504const CoreManager& System::GetCoreManager(std::size_t core_index) const {
505 ASSERT(core_index < NUM_CPU_CORES);
506 return impl->cpu_manager.GetCoreManager(core_index);
507}
508
509ExclusiveMonitor& System::Monitor() { 512ExclusiveMonitor& System::Monitor() {
510 return impl->kernel.GetExclusiveMonitor(); 513 return impl->kernel.GetExclusiveMonitor();
511} 514}
diff --git a/src/core/core.h b/src/core/core.h
index acc53d6a1..7f170fc54 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -90,7 +90,7 @@ class InterruptManager;
90namespace Core { 90namespace Core {
91 91
92class ARM_Interface; 92class ARM_Interface;
93class CoreManager; 93class CpuManager;
94class DeviceMemory; 94class DeviceMemory;
95class ExclusiveMonitor; 95class ExclusiveMonitor;
96class FrameLimiter; 96class FrameLimiter;
@@ -136,16 +136,18 @@ public:
136 }; 136 };
137 137
138 /** 138 /**
139 * Run the core CPU loop 139 * Run the OS and Application
140 * This function runs the core for the specified number of CPU instructions before trying to 140 * This function will start emulation and run the competent devices
141 * update hardware. This is much faster than SingleStep (and should be equivalent), as the CPU 141 */
142 * is not required to do a full dispatch with each instruction. NOTE: the number of instructions 142 ResultStatus Run();
143 * requested is not guaranteed to run, as this will be interrupted preemptively if a hardware 143
144 * update is requested (e.g. on a thread switch). 144 /**
145 * @param tight_loop If false, the CPU single-steps. 145 * Pause the OS and Application
146 * @return Result status, indicating whether or not the operation succeeded. 146 * This function will pause emulation and stop the competent devices
147 */ 147 */
148 ResultStatus RunLoop(bool tight_loop = true); 148 ResultStatus Pause();
149
150
149 151
150 /** 152 /**
151 * Step the CPU one instruction 153 * Step the CPU one instruction
@@ -215,11 +217,9 @@ public:
215 /// Gets a const reference to an ARM interface from the CPU core with the specified index 217 /// Gets a const reference to an ARM interface from the CPU core with the specified index
216 const ARM_Interface& ArmInterface(std::size_t core_index) const; 218 const ARM_Interface& ArmInterface(std::size_t core_index) const;
217 219
218 /// Gets a CPU interface to the CPU core with the specified index 220 CpuManager& GetCpuManager();
219 CoreManager& GetCoreManager(std::size_t core_index);
220 221
221 /// Gets a CPU interface to the CPU core with the specified index 222 const CpuManager& GetCpuManager() const;
222 const CoreManager& GetCoreManager(std::size_t core_index) const;
223 223
224 /// Gets a reference to the exclusive monitor 224 /// Gets a reference to the exclusive monitor
225 ExclusiveMonitor& Monitor(); 225 ExclusiveMonitor& Monitor();
@@ -373,12 +373,6 @@ public:
373private: 373private:
374 System(); 374 System();
375 375
376 /// Returns the currently running CPU core
377 CoreManager& CurrentCoreManager();
378
379 /// Returns the currently running CPU core
380 const CoreManager& CurrentCoreManager() const;
381
382 /** 376 /**
383 * Initialize the emulated system. 377 * Initialize the emulated system.
384 * @param emu_window Reference to the host-system window used for video output and keyboard 378 * @param emu_window Reference to the host-system window used for video output and keyboard
diff --git a/src/core/core_manager.cpp b/src/core/core_manager.cpp
index b6b797c80..45f0bb547 100644
--- a/src/core/core_manager.cpp
+++ b/src/core/core_manager.cpp
@@ -34,7 +34,6 @@ void CoreManager::RunLoop(bool tight_loop) {
34 // instead advance to the next event and try to yield to the next thread 34 // instead advance to the next event and try to yield to the next thread
35 if (Kernel::GetCurrentThread() == nullptr) { 35 if (Kernel::GetCurrentThread() == nullptr) {
36 LOG_TRACE(Core, "Core-{} idling", core_index); 36 LOG_TRACE(Core, "Core-{} idling", core_index);
37 core_timing.Idle();
38 } else { 37 } else {
39 if (tight_loop) { 38 if (tight_loop) {
40 physical_core.Run(); 39 physical_core.Run();
@@ -42,7 +41,6 @@ void CoreManager::RunLoop(bool tight_loop) {
42 physical_core.Step(); 41 physical_core.Step();
43 } 42 }
44 } 43 }
45 core_timing.Advance();
46 44
47 Reschedule(); 45 Reschedule();
48} 46}
@@ -59,7 +57,7 @@ void CoreManager::Reschedule() {
59 // Lock the global kernel mutex when we manipulate the HLE state 57 // Lock the global kernel mutex when we manipulate the HLE state
60 std::lock_guard lock(HLE::g_hle_lock); 58 std::lock_guard lock(HLE::g_hle_lock);
61 59
62 global_scheduler.SelectThread(core_index); 60 // global_scheduler.SelectThread(core_index);
63 61
64 physical_core.Scheduler().TryDoContextSwitch(); 62 physical_core.Scheduler().TryDoContextSwitch();
65} 63}
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index 46d4178c4..a3ce69790 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -1,5 +1,5 @@
1// Copyright 2008 Dolphin Emulator Project / 2017 Citra Emulator Project 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2+ 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 "core/core_timing.h" 5#include "core/core_timing.h"
@@ -10,20 +10,16 @@
10#include <tuple> 10#include <tuple>
11 11
12#include "common/assert.h" 12#include "common/assert.h"
13#include "common/thread.h"
14#include "core/core_timing_util.h" 13#include "core/core_timing_util.h"
15#include "core/hardware_properties.h"
16 14
17namespace Core::Timing { 15namespace Core::Timing {
18 16
19constexpr int MAX_SLICE_LENGTH = 10000;
20
21std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) { 17std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callback) {
22 return std::make_shared<EventType>(std::move(callback), std::move(name)); 18 return std::make_shared<EventType>(std::move(callback), std::move(name));
23} 19}
24 20
25struct CoreTiming::Event { 21struct CoreTiming::Event {
26 s64 time; 22 u64 time;
27 u64 fifo_order; 23 u64 fifo_order;
28 u64 userdata; 24 u64 userdata;
29 std::weak_ptr<EventType> type; 25 std::weak_ptr<EventType> type;
@@ -39,51 +35,74 @@ struct CoreTiming::Event {
39 } 35 }
40}; 36};
41 37
42CoreTiming::CoreTiming() = default; 38CoreTiming::CoreTiming() {
43CoreTiming::~CoreTiming() = default; 39 clock =
40 Common::CreateBestMatchingClock(Core::Hardware::BASE_CLOCK_RATE, Core::Hardware::CNTFREQ);
41}
44 42
45void CoreTiming::Initialize() { 43CoreTiming::~CoreTiming() = default;
46 downcounts.fill(MAX_SLICE_LENGTH);
47 time_slice.fill(MAX_SLICE_LENGTH);
48 slice_length = MAX_SLICE_LENGTH;
49 global_timer = 0;
50 idled_cycles = 0;
51 current_context = 0;
52 44
53 // The time between CoreTiming being initialized and the first call to Advance() is considered 45void CoreTiming::ThreadEntry(CoreTiming& instance) {
54 // the slice boundary between slice -1 and slice 0. Dispatcher loops must call Advance() before 46 std::string name = "yuzu:HostTiming";
55 // executing the first cycle of each slice to prepare the slice length and downcount for 47 Common::SetCurrentThreadName(name.c_str());
56 // that slice. 48 instance.on_thread_init();
57 is_global_timer_sane = true; 49 instance.ThreadLoop();
50}
58 51
52void CoreTiming::Initialize(std::function<void(void)>&& on_thread_init_) {
53 on_thread_init = std::move(on_thread_init_);
59 event_fifo_id = 0; 54 event_fifo_id = 0;
60
61 const auto empty_timed_callback = [](u64, s64) {}; 55 const auto empty_timed_callback = [](u64, s64) {};
62 ev_lost = CreateEvent("_lost_event", empty_timed_callback); 56 ev_lost = CreateEvent("_lost_event", empty_timed_callback);
57 timer_thread = std::make_unique<std::thread>(ThreadEntry, std::ref(*this));
63} 58}
64 59
65void CoreTiming::Shutdown() { 60void CoreTiming::Shutdown() {
61 paused = true;
62 shutting_down = true;
63 event.Set();
64 timer_thread->join();
66 ClearPendingEvents(); 65 ClearPendingEvents();
66 timer_thread.reset();
67 has_started = false;
67} 68}
68 69
69void CoreTiming::ScheduleEvent(s64 cycles_into_future, const std::shared_ptr<EventType>& event_type, 70void CoreTiming::Pause(bool is_paused) {
70 u64 userdata) { 71 paused = is_paused;
71 std::lock_guard guard{inner_mutex}; 72}
72 const s64 timeout = GetTicks() + cycles_into_future;
73 73
74 // If this event needs to be scheduled before the next advance(), force one early 74void CoreTiming::SyncPause(bool is_paused) {
75 if (!is_global_timer_sane) { 75 if (is_paused == paused && paused_set == paused) {
76 ForceExceptionCheck(cycles_into_future); 76 return;
77 } 77 }
78 Pause(is_paused);
79 event.Set();
80 while (paused_set != is_paused)
81 ;
82}
83
84bool CoreTiming::IsRunning() const {
85 return !paused_set;
86}
87
88bool CoreTiming::HasPendingEvents() const {
89 return !(wait_set && event_queue.empty());
90}
91
92void CoreTiming::ScheduleEvent(s64 ns_into_future, const std::shared_ptr<EventType>& event_type,
93 u64 userdata) {
94 basic_lock.lock();
95 const u64 timeout = static_cast<u64>(GetGlobalTimeNs().count() + ns_into_future);
78 96
79 event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type}); 97 event_queue.emplace_back(Event{timeout, event_fifo_id++, userdata, event_type});
80 98
81 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>()); 99 std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
100 basic_lock.unlock();
101 event.Set();
82} 102}
83 103
84void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata) { 104void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata) {
85 std::lock_guard guard{inner_mutex}; 105 basic_lock.lock();
86
87 const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { 106 const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
88 return e.type.lock().get() == event_type.get() && e.userdata == userdata; 107 return e.type.lock().get() == event_type.get() && e.userdata == userdata;
89 }); 108 });
@@ -93,23 +112,23 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u
93 event_queue.erase(itr, event_queue.end()); 112 event_queue.erase(itr, event_queue.end());
94 std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); 113 std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
95 } 114 }
115 basic_lock.unlock();
96} 116}
97 117
98u64 CoreTiming::GetTicks() const { 118void CoreTiming::AddTicks(std::size_t core_index, u64 ticks) {
99 u64 ticks = static_cast<u64>(global_timer); 119 ticks_count[core_index] += ticks;
100 if (!is_global_timer_sane) { 120}
101 ticks += accumulated_ticks; 121
102 } 122void CoreTiming::ResetTicks(std::size_t core_index) {
103 return ticks; 123 ticks_count[core_index] = 0;
104} 124}
105 125
106u64 CoreTiming::GetIdleTicks() const { 126u64 CoreTiming::GetCPUTicks() const {
107 return static_cast<u64>(idled_cycles); 127 return clock->GetCPUCycles();
108} 128}
109 129
110void CoreTiming::AddTicks(u64 ticks) { 130u64 CoreTiming::GetClockTicks() const {
111 accumulated_ticks += ticks; 131 return clock->GetClockCycles();
112 downcounts[current_context] -= static_cast<s64>(ticks);
113} 132}
114 133
115void CoreTiming::ClearPendingEvents() { 134void CoreTiming::ClearPendingEvents() {
@@ -117,7 +136,7 @@ void CoreTiming::ClearPendingEvents() {
117} 136}
118 137
119void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) { 138void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
120 std::lock_guard guard{inner_mutex}; 139 basic_lock.lock();
121 140
122 const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) { 141 const auto itr = std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
123 return e.type.lock().get() == event_type.get(); 142 return e.type.lock().get() == event_type.get();
@@ -128,99 +147,64 @@ void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
128 event_queue.erase(itr, event_queue.end()); 147 event_queue.erase(itr, event_queue.end());
129 std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>()); 148 std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
130 } 149 }
150 basic_lock.unlock();
131} 151}
132 152
133void CoreTiming::ForceExceptionCheck(s64 cycles) { 153std::optional<u64> CoreTiming::Advance() {
134 cycles = std::max<s64>(0, cycles); 154 advance_lock.lock();
135 if (downcounts[current_context] <= cycles) { 155 basic_lock.lock();
136 return; 156 global_timer = GetGlobalTimeNs().count();
137 }
138
139 // downcount is always (much) smaller than MAX_INT so we can safely cast cycles to an int
140 // here. Account for cycles already executed by adjusting the g.slice_length
141 downcounts[current_context] = static_cast<int>(cycles);
142}
143
144std::optional<u64> CoreTiming::NextAvailableCore(const s64 needed_ticks) const {
145 const u64 original_context = current_context;
146 u64 next_context = (original_context + 1) % num_cpu_cores;
147 while (next_context != original_context) {
148 if (time_slice[next_context] >= needed_ticks) {
149 return {next_context};
150 } else if (time_slice[next_context] >= 0) {
151 return std::nullopt;
152 }
153 next_context = (next_context + 1) % num_cpu_cores;
154 }
155 return std::nullopt;
156}
157
158void CoreTiming::Advance() {
159 std::unique_lock<std::mutex> guard(inner_mutex);
160
161 const u64 cycles_executed = accumulated_ticks;
162 time_slice[current_context] = std::max<s64>(0, time_slice[current_context] - accumulated_ticks);
163 global_timer += cycles_executed;
164
165 is_global_timer_sane = true;
166 157
167 while (!event_queue.empty() && event_queue.front().time <= global_timer) { 158 while (!event_queue.empty() && event_queue.front().time <= global_timer) {
168 Event evt = std::move(event_queue.front()); 159 Event evt = std::move(event_queue.front());
169 std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>()); 160 std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>());
170 event_queue.pop_back(); 161 event_queue.pop_back();
171 inner_mutex.unlock(); 162 basic_lock.unlock();
172 163
173 if (auto event_type{evt.type.lock()}) { 164 if (auto event_type{evt.type.lock()}) {
174 event_type->callback(evt.userdata, global_timer - evt.time); 165 event_type->callback(evt.userdata, global_timer - evt.time);
175 } 166 }
176 167
177 inner_mutex.lock(); 168 basic_lock.lock();
178 } 169 }
179 170
180 is_global_timer_sane = false;
181
182 // Still events left (scheduled in the future)
183 if (!event_queue.empty()) { 171 if (!event_queue.empty()) {
184 const s64 needed_ticks = 172 const u64 next_time = event_queue.front().time - global_timer;
185 std::min<s64>(event_queue.front().time - global_timer, MAX_SLICE_LENGTH); 173 basic_lock.unlock();
186 const auto next_core = NextAvailableCore(needed_ticks); 174 advance_lock.unlock();
187 if (next_core) { 175 return next_time;
188 downcounts[*next_core] = needed_ticks; 176 } else {
189 } 177 basic_lock.unlock();
178 advance_lock.unlock();
179 return std::nullopt;
190 } 180 }
191
192 accumulated_ticks = 0;
193
194 downcounts[current_context] = time_slice[current_context];
195} 181}
196 182
197void CoreTiming::ResetRun() { 183void CoreTiming::ThreadLoop() {
198 downcounts.fill(MAX_SLICE_LENGTH); 184 has_started = true;
199 time_slice.fill(MAX_SLICE_LENGTH); 185 while (!shutting_down) {
200 current_context = 0; 186 while (!paused) {
201 // Still events left (scheduled in the future) 187 paused_set = false;
202 if (!event_queue.empty()) { 188 const auto next_time = Advance();
203 const s64 needed_ticks = 189 if (next_time) {
204 std::min<s64>(event_queue.front().time - global_timer, MAX_SLICE_LENGTH); 190 std::chrono::nanoseconds next_time_ns = std::chrono::nanoseconds(*next_time);
205 downcounts[current_context] = needed_ticks; 191 event.WaitFor(next_time_ns);
192 } else {
193 wait_set = true;
194 event.Wait();
195 }
196 wait_set = false;
197 }
198 paused_set = true;
206 } 199 }
207
208 is_global_timer_sane = false;
209 accumulated_ticks = 0;
210} 200}
211 201
212void CoreTiming::Idle() { 202std::chrono::nanoseconds CoreTiming::GetGlobalTimeNs() const {
213 accumulated_ticks += downcounts[current_context]; 203 return clock->GetTimeNS();
214 idled_cycles += downcounts[current_context];
215 downcounts[current_context] = 0;
216} 204}
217 205
218std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const { 206std::chrono::microseconds CoreTiming::GetGlobalTimeUs() const {
219 return std::chrono::microseconds{GetTicks() * 1000000 / Hardware::BASE_CLOCK_RATE}; 207 return clock->GetTimeUS();
220}
221
222s64 CoreTiming::GetDowncount() const {
223 return downcounts[current_context];
224} 208}
225 209
226} // namespace Core::Timing 210} // namespace Core::Timing
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index d50f4eb8a..707c8ef0c 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -1,19 +1,25 @@
1// Copyright 2008 Dolphin Emulator Project / 2017 Citra Emulator Project 1// Copyright 2020 yuzu Emulator Project
2// Licensed under GPLv2+ 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#pragma once 5#pragma once
6 6
7#include <atomic>
7#include <chrono> 8#include <chrono>
8#include <functional> 9#include <functional>
9#include <memory> 10#include <memory>
10#include <mutex> 11#include <mutex>
11#include <optional> 12#include <optional>
12#include <string> 13#include <string>
14#include <thread>
13#include <vector> 15#include <vector>
14 16
15#include "common/common_types.h" 17#include "common/common_types.h"
18#include "common/spin_lock.h"
19#include "common/thread.h"
16#include "common/threadsafe_queue.h" 20#include "common/threadsafe_queue.h"
21#include "common/wall_clock.h"
22#include "core/hardware_properties.h"
17 23
18namespace Core::Timing { 24namespace Core::Timing {
19 25
@@ -56,58 +62,55 @@ public:
56 62
57 /// CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is 63 /// CoreTiming begins at the boundary of timing slice -1. An initial call to Advance() is
58 /// required to end slice - 1 and start slice 0 before the first cycle of code is executed. 64 /// required to end slice - 1 and start slice 0 before the first cycle of code is executed.
59 void Initialize(); 65 void Initialize(std::function<void(void)>&& on_thread_init_);
60 66
61 /// Tears down all timing related functionality. 67 /// Tears down all timing related functionality.
62 void Shutdown(); 68 void Shutdown();
63 69
64 /// After the first Advance, the slice lengths and the downcount will be reduced whenever an 70 /// Pauses/Unpauses the execution of the timer thread.
65 /// event is scheduled earlier than the current values. 71 void Pause(bool is_paused);
66 ///
67 /// Scheduling from a callback will not update the downcount until the Advance() completes.
68 void ScheduleEvent(s64 cycles_into_future, const std::shared_ptr<EventType>& event_type,
69 u64 userdata = 0);
70 72
71 void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata); 73 /// Pauses/Unpauses the execution of the timer thread and waits until paused.
74 void SyncPause(bool is_paused);
72 75
73 /// We only permit one event of each type in the queue at a time. 76 /// Checks if core timing is running.
74 void RemoveEvent(const std::shared_ptr<EventType>& event_type); 77 bool IsRunning() const;
75 78
76 void ForceExceptionCheck(s64 cycles); 79 /// Checks if the timer thread has started.
80 bool HasStarted() const {
81 return has_started;
82 }
77 83
78 /// This should only be called from the emu thread, if you are calling it any other thread, 84 /// Checks if there are any pending time events.
79 /// you are doing something evil 85 bool HasPendingEvents() const;
80 u64 GetTicks() const;
81 86
82 u64 GetIdleTicks() const; 87 /// Schedules an event in core timing
88 void ScheduleEvent(s64 ns_into_future, const std::shared_ptr<EventType>& event_type,
89 u64 userdata = 0);
83 90
84 void AddTicks(u64 ticks); 91 void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, u64 userdata);
85 92
86 /// Advance must be called at the beginning of dispatcher loops, not the end. Advance() ends 93 /// We only permit one event of each type in the queue at a time.
87 /// the previous timing slice and begins the next one, you must Advance from the previous 94 void RemoveEvent(const std::shared_ptr<EventType>& event_type);
88 /// slice to the current one before executing any cycles. CoreTiming starts in slice -1 so an
89 /// Advance() is required to initialize the slice length before the first cycle of emulated
90 /// instructions is executed.
91 void Advance();
92 95
93 /// Pretend that the main CPU has executed enough cycles to reach the next event. 96 void AddTicks(std::size_t core_index, u64 ticks);
94 void Idle();
95 97
96 std::chrono::microseconds GetGlobalTimeUs() const; 98 void ResetTicks(std::size_t core_index);
97 99
98 void ResetRun(); 100 /// Returns current time in emulated CPU cycles
101 u64 GetCPUTicks() const;
99 102
100 s64 GetDowncount() const; 103 /// Returns current time in emulated in Clock cycles
104 u64 GetClockTicks() const;
101 105
102 void SwitchContext(u64 new_context) { 106 /// Returns current time in microseconds.
103 current_context = new_context; 107 std::chrono::microseconds GetGlobalTimeUs() const;
104 }
105 108
106 bool CanCurrentContextRun() const { 109 /// Returns current time in nanoseconds.
107 return time_slice[current_context] > 0; 110 std::chrono::nanoseconds GetGlobalTimeNs() const;
108 }
109 111
110 std::optional<u64> NextAvailableCore(const s64 needed_ticks) const; 112 /// Checks for events manually and returns time in nanoseconds for next event, threadsafe.
113 std::optional<u64> Advance();
111 114
112private: 115private:
113 struct Event; 116 struct Event;
@@ -115,21 +118,14 @@ private:
115 /// Clear all pending events. This should ONLY be done on exit. 118 /// Clear all pending events. This should ONLY be done on exit.
116 void ClearPendingEvents(); 119 void ClearPendingEvents();
117 120
118 static constexpr u64 num_cpu_cores = 4; 121 static void ThreadEntry(CoreTiming& instance);
122 void ThreadLoop();
123
124 std::unique_ptr<Common::WallClock> clock;
119 125
120 s64 global_timer = 0; 126 u64 global_timer = 0;
121 s64 idled_cycles = 0;
122 s64 slice_length = 0;
123 u64 accumulated_ticks = 0;
124 std::array<s64, num_cpu_cores> downcounts{};
125 // Slice of time assigned to each core per run.
126 std::array<s64, num_cpu_cores> time_slice{};
127 u64 current_context = 0;
128 127
129 // Are we in a function that has been called from Advance() 128 std::chrono::nanoseconds start_point;
130 // If events are scheduled from a function that gets called from Advance(),
131 // don't change slice_length and downcount.
132 bool is_global_timer_sane = false;
133 129
134 // The queue is a min-heap using std::make_heap/push_heap/pop_heap. 130 // The queue is a min-heap using std::make_heap/push_heap/pop_heap.
135 // We don't use std::priority_queue because we need to be able to serialize, unserialize and 131 // We don't use std::priority_queue because we need to be able to serialize, unserialize and
@@ -139,8 +135,18 @@ private:
139 u64 event_fifo_id = 0; 135 u64 event_fifo_id = 0;
140 136
141 std::shared_ptr<EventType> ev_lost; 137 std::shared_ptr<EventType> ev_lost;
142 138 Common::Event event{};
143 std::mutex inner_mutex; 139 Common::SpinLock basic_lock{};
140 Common::SpinLock advance_lock{};
141 std::unique_ptr<std::thread> timer_thread;
142 std::atomic<bool> paused{};
143 std::atomic<bool> paused_set{};
144 std::atomic<bool> wait_set{};
145 std::atomic<bool> shutting_down{};
146 std::atomic<bool> has_started{};
147 std::function<void(void)> on_thread_init{};
148
149 std::array<std::atomic<u64>, Core::Hardware::NUM_CPU_CORES> ticks_count{};
144}; 150};
145 151
146/// Creates a core timing event with the given name and callback. 152/// Creates a core timing event with the given name and callback.
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index 70ddbdcca..494850992 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -2,80 +2,192 @@
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/fiber.h"
6#include "common/thread.h"
5#include "core/arm/exclusive_monitor.h" 7#include "core/arm/exclusive_monitor.h"
6#include "core/core.h" 8#include "core/core.h"
7#include "core/core_manager.h"
8#include "core/core_timing.h" 9#include "core/core_timing.h"
9#include "core/cpu_manager.h" 10#include "core/cpu_manager.h"
10#include "core/gdbstub/gdbstub.h" 11#include "core/gdbstub/gdbstub.h"
12#include "core/hle/kernel/kernel.h"
13#include "core/hle/kernel/physical_core.h"
14#include "core/hle/kernel/scheduler.h"
15#include "core/hle/kernel/thread.h"
11 16
12namespace Core { 17namespace Core {
13 18
14CpuManager::CpuManager(System& system) : system{system} {} 19CpuManager::CpuManager(System& system) : system{system} {}
15CpuManager::~CpuManager() = default; 20CpuManager::~CpuManager() = default;
16 21
22void CpuManager::ThreadStart(CpuManager& cpu_manager, std::size_t core) {
23 cpu_manager.RunThread(core);
24}
25
17void CpuManager::Initialize() { 26void CpuManager::Initialize() {
18 for (std::size_t index = 0; index < core_managers.size(); ++index) { 27 running_mode = true;
19 core_managers[index] = std::make_unique<CoreManager>(system, index); 28 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
29 core_data[core].host_thread =
30 std::make_unique<std::thread>(ThreadStart, std::ref(*this), core);
20 } 31 }
21} 32}
22 33
23void CpuManager::Shutdown() { 34void CpuManager::Shutdown() {
24 for (auto& cpu_core : core_managers) { 35 running_mode = false;
25 cpu_core.reset(); 36 Pause(false);
37 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
38 core_data[core].host_thread->join();
26 } 39 }
27} 40}
28 41
29CoreManager& CpuManager::GetCoreManager(std::size_t index) { 42void CpuManager::GuestThreadFunction(void* cpu_manager_) {
30 return *core_managers.at(index); 43 CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
44 cpu_manager->RunGuestThread();
31} 45}
32 46
33const CoreManager& CpuManager::GetCoreManager(std::size_t index) const { 47void CpuManager::IdleThreadFunction(void* cpu_manager_) {
34 return *core_managers.at(index); 48 CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
49 cpu_manager->RunIdleThread();
35} 50}
36 51
37CoreManager& CpuManager::GetCurrentCoreManager() { 52void CpuManager::SuspendThreadFunction(void* cpu_manager_) {
38 // Otherwise, use single-threaded mode active_core variable 53 CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
39 return *core_managers[active_core]; 54 cpu_manager->RunSuspendThread();
40} 55}
41 56
42const CoreManager& CpuManager::GetCurrentCoreManager() const { 57std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() {
43 // Otherwise, use single-threaded mode active_core variable 58 return std::function<void(void*)>(GuestThreadFunction);
44 return *core_managers[active_core];
45} 59}
46 60
47void CpuManager::RunLoop(bool tight_loop) { 61std::function<void(void*)> CpuManager::GetIdleThreadStartFunc() {
48 if (GDBStub::IsServerEnabled()) { 62 return std::function<void(void*)>(IdleThreadFunction);
49 GDBStub::HandlePacket(); 63}
50 64
51 // If the loop is halted and we want to step, use a tiny (1) number of instructions to 65std::function<void(void*)> CpuManager::GetSuspendThreadStartFunc() {
52 // execute. Otherwise, get out of the loop function. 66 return std::function<void(void*)>(SuspendThreadFunction);
53 if (GDBStub::GetCpuHaltFlag()) { 67}
54 if (GDBStub::GetCpuStepFlag()) { 68
55 tight_loop = false; 69void* CpuManager::GetStartFuncParamater() {
56 } else { 70 return static_cast<void*>(this);
57 return; 71}
58 } 72
59 } 73void CpuManager::RunGuestThread() {
74 auto& kernel = system.Kernel();
75 {
76 auto& sched = kernel.CurrentScheduler();
77 sched.OnThreadStart();
78 }
79 while (true) {
80 auto& physical_core = kernel.CurrentPhysicalCore();
81 LOG_CRITICAL(Core_ARM, "Running Guest Thread");
82 physical_core.Idle();
83 LOG_CRITICAL(Core_ARM, "Leaving Guest Thread");
84 // physical_core.Run();
85 auto& scheduler = physical_core.Scheduler();
86 scheduler.TryDoContextSwitch();
60 } 87 }
88}
61 89
62 auto& core_timing = system.CoreTiming(); 90void CpuManager::RunIdleThread() {
63 core_timing.ResetRun(); 91 auto& kernel = system.Kernel();
64 bool keep_running{}; 92 while (true) {
65 do { 93 auto& physical_core = kernel.CurrentPhysicalCore();
66 keep_running = false; 94 LOG_CRITICAL(Core_ARM, "Running Idle Thread");
67 for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) { 95 physical_core.Idle();
68 core_timing.SwitchContext(active_core); 96 auto& scheduler = physical_core.Scheduler();
69 if (core_timing.CanCurrentContextRun()) { 97 scheduler.TryDoContextSwitch();
70 core_managers[active_core]->RunLoop(tight_loop); 98 }
99}
100
101void CpuManager::RunSuspendThread() {
102 LOG_CRITICAL(Core_ARM, "Suspending Thread Entered");
103 auto& kernel = system.Kernel();
104 {
105 auto& sched = kernel.CurrentScheduler();
106 sched.OnThreadStart();
107 }
108 while (true) {
109 auto core = kernel.GetCurrentHostThreadID();
110 auto& scheduler = kernel.CurrentScheduler();
111 Kernel::Thread* current_thread = scheduler.GetCurrentThread();
112 LOG_CRITICAL(Core_ARM, "Suspending Core {}", core);
113 Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[core].host_context);
114 LOG_CRITICAL(Core_ARM, "Unsuspending Core {}", core);
115 ASSERT(scheduler.ContextSwitchPending());
116 ASSERT(core == kernel.GetCurrentHostThreadID());
117 scheduler.TryDoContextSwitch();
118 }
119}
120
121void CpuManager::Pause(bool paused) {
122 if (!paused) {
123 bool all_not_barrier = false;
124 while (!all_not_barrier) {
125 all_not_barrier = true;
126 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
127 all_not_barrier &=
128 !core_data[core].is_running.load() && core_data[core].initialized.load();
71 } 129 }
72 keep_running |= core_timing.CanCurrentContextRun();
73 } 130 }
74 } while (keep_running); 131 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
132 core_data[core].enter_barrier->Set();
133 }
134 if (paused_state.load()) {
135 bool all_barrier = false;
136 while (!all_barrier) {
137 all_barrier = true;
138 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
139 all_barrier &=
140 core_data[core].is_paused.load() && core_data[core].initialized.load();
141 }
142 }
143 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
144 core_data[core].exit_barrier->Set();
145 }
146 }
147 } else {
148 /// Wait until all cores are paused.
149 bool all_barrier = false;
150 while (!all_barrier) {
151 all_barrier = true;
152 for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
153 all_barrier &=
154 core_data[core].is_paused.load() && core_data[core].initialized.load();
155 }
156 }
157 /// Don't release the barrier
158 }
159 paused_state = paused;
160}
75 161
76 if (GDBStub::IsServerEnabled()) { 162void CpuManager::RunThread(std::size_t core) {
77 GDBStub::SetCpuStepFlag(false); 163 /// Initialization
164 system.RegisterCoreThread(core);
165 std::string name = "yuzu:CoreHostThread_" + std::to_string(core);
166 Common::SetCurrentThreadName(name.c_str());
167 auto& data = core_data[core];
168 data.enter_barrier = std::make_unique<Common::Event>();
169 data.exit_barrier = std::make_unique<Common::Event>();
170 data.host_context = Common::Fiber::ThreadToFiber();
171 data.is_running = false;
172 data.initialized = true;
173 /// Running
174 while (running_mode) {
175 data.is_running = false;
176 data.enter_barrier->Wait();
177 auto& scheduler = system.Kernel().CurrentScheduler();
178 Kernel::Thread* current_thread = scheduler.GetCurrentThread();
179 data.is_running = true;
180 Common::Fiber::YieldTo(data.host_context, current_thread->GetHostContext());
181 data.is_running = false;
182 data.is_paused = true;
183 data.exit_barrier->Wait();
184 data.is_paused = false;
78 } 185 }
186 /// Time to cleanup
187 data.host_context->Exit();
188 data.enter_barrier.reset();
189 data.exit_barrier.reset();
190 data.initialized = false;
79} 191}
80 192
81} // namespace Core 193} // namespace Core
diff --git a/src/core/cpu_manager.h b/src/core/cpu_manager.h
index 97554d1bb..8103ae857 100644
--- a/src/core/cpu_manager.h
+++ b/src/core/cpu_manager.h
@@ -5,12 +5,18 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <functional>
8#include <memory> 9#include <memory>
10#include <thread>
9#include "core/hardware_properties.h" 11#include "core/hardware_properties.h"
10 12
13namespace Common {
14class Event;
15class Fiber;
16} // namespace Common
17
11namespace Core { 18namespace Core {
12 19
13class CoreManager;
14class System; 20class System;
15 21
16class CpuManager { 22class CpuManager {
@@ -27,21 +33,40 @@ public:
27 void Initialize(); 33 void Initialize();
28 void Shutdown(); 34 void Shutdown();
29 35
30 CoreManager& GetCoreManager(std::size_t index); 36 void Pause(bool paused);
31 const CoreManager& GetCoreManager(std::size_t index) const; 37
38 std::function<void(void*)> GetGuestThreadStartFunc();
39 std::function<void(void*)> GetIdleThreadStartFunc();
40 std::function<void(void*)> GetSuspendThreadStartFunc();
41 void* GetStartFuncParamater();
32 42
33 CoreManager& GetCurrentCoreManager(); 43private:
34 const CoreManager& GetCurrentCoreManager() const; 44 static void GuestThreadFunction(void* cpu_manager);
45 static void IdleThreadFunction(void* cpu_manager);
46 static void SuspendThreadFunction(void* cpu_manager);
35 47
36 std::size_t GetActiveCoreIndex() const { 48 void RunGuestThread();
37 return active_core; 49 void RunIdleThread();
38 } 50 void RunSuspendThread();
39 51
40 void RunLoop(bool tight_loop); 52 static void ThreadStart(CpuManager& cpu_manager, std::size_t core);
41 53
42private: 54 void RunThread(std::size_t core);
43 std::array<std::unique_ptr<CoreManager>, Hardware::NUM_CPU_CORES> core_managers; 55
44 std::size_t active_core{}; ///< Active core, only used in single thread mode 56 struct CoreData {
57 std::shared_ptr<Common::Fiber> host_context;
58 std::unique_ptr<Common::Event> enter_barrier;
59 std::unique_ptr<Common::Event> exit_barrier;
60 std::atomic<bool> is_running;
61 std::atomic<bool> is_paused;
62 std::atomic<bool> initialized;
63 std::unique_ptr<std::thread> host_thread;
64 };
65
66 std::atomic<bool> running_mode{};
67 std::atomic<bool> paused_state{};
68
69 std::array<CoreData, Core::Hardware::NUM_CPU_CORES> core_data{};
45 70
46 System& system; 71 System& system;
47}; 72};
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
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
index 1f2131ec8..cb35919e9 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.cpp
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -23,7 +23,7 @@ void Controller_DebugPad::OnRelease() {}
23 23
24void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 24void Controller_DebugPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
25 std::size_t size) { 25 std::size_t size) {
26 shared_memory.header.timestamp = core_timing.GetTicks(); 26 shared_memory.header.timestamp = core_timing.GetCPUTicks();
27 shared_memory.header.total_entry_count = 17; 27 shared_memory.header.total_entry_count = 17;
28 28
29 if (!IsControllerActivated()) { 29 if (!IsControllerActivated()) {
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
index 6e990dd00..b7b7bfeae 100644
--- a/src/core/hle/service/hid/controllers/gesture.cpp
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -19,7 +19,7 @@ void Controller_Gesture::OnRelease() {}
19 19
20void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 20void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
21 std::size_t size) { 21 std::size_t size) {
22 shared_memory.header.timestamp = core_timing.GetTicks(); 22 shared_memory.header.timestamp = core_timing.GetCPUTicks();
23 shared_memory.header.total_entry_count = 17; 23 shared_memory.header.total_entry_count = 17;
24 24
25 if (!IsControllerActivated()) { 25 if (!IsControllerActivated()) {
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
index 9a8d354ba..feae89525 100644
--- a/src/core/hle/service/hid/controllers/keyboard.cpp
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -21,7 +21,7 @@ void Controller_Keyboard::OnRelease() {}
21 21
22void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 22void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
23 std::size_t size) { 23 std::size_t size) {
24 shared_memory.header.timestamp = core_timing.GetTicks(); 24 shared_memory.header.timestamp = core_timing.GetCPUTicks();
25 shared_memory.header.total_entry_count = 17; 25 shared_memory.header.total_entry_count = 17;
26 26
27 if (!IsControllerActivated()) { 27 if (!IsControllerActivated()) {
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp
index 93d88ea50..ac40989c5 100644
--- a/src/core/hle/service/hid/controllers/mouse.cpp
+++ b/src/core/hle/service/hid/controllers/mouse.cpp
@@ -19,7 +19,7 @@ void Controller_Mouse::OnRelease() {}
19 19
20void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 20void Controller_Mouse::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
21 std::size_t size) { 21 std::size_t size) {
22 shared_memory.header.timestamp = core_timing.GetTicks(); 22 shared_memory.header.timestamp = core_timing.GetCPUTicks();
23 shared_memory.header.total_entry_count = 17; 23 shared_memory.header.total_entry_count = 17;
24 24
25 if (!IsControllerActivated()) { 25 if (!IsControllerActivated()) {
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 6fbee7efa..ef67ad690 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -328,7 +328,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
328 const auto& last_entry = 328 const auto& last_entry =
329 main_controller->npad[main_controller->common.last_entry_index]; 329 main_controller->npad[main_controller->common.last_entry_index];
330 330
331 main_controller->common.timestamp = core_timing.GetTicks(); 331 main_controller->common.timestamp = core_timing.GetCPUTicks();
332 main_controller->common.last_entry_index = 332 main_controller->common.last_entry_index =
333 (main_controller->common.last_entry_index + 1) % 17; 333 (main_controller->common.last_entry_index + 1) % 17;
334 334
diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp
index 9e527d176..e7483bfa2 100644
--- a/src/core/hle/service/hid/controllers/stubbed.cpp
+++ b/src/core/hle/service/hid/controllers/stubbed.cpp
@@ -23,7 +23,7 @@ void Controller_Stubbed::OnUpdate(const Core::Timing::CoreTiming& core_timing, u
23 } 23 }
24 24
25 CommonHeader header{}; 25 CommonHeader header{};
26 header.timestamp = core_timing.GetTicks(); 26 header.timestamp = core_timing.GetCPUTicks();
27 header.total_entry_count = 17; 27 header.total_entry_count = 17;
28 header.entry_count = 0; 28 header.entry_count = 0;
29 header.last_entry_index = 0; 29 header.last_entry_index = 0;
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index 1c6e55566..e326f8f5c 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -22,7 +22,7 @@ void Controller_Touchscreen::OnRelease() {}
22 22
23void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 23void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
24 std::size_t size) { 24 std::size_t size) {
25 shared_memory.header.timestamp = core_timing.GetTicks(); 25 shared_memory.header.timestamp = core_timing.GetCPUTicks();
26 shared_memory.header.total_entry_count = 17; 26 shared_memory.header.total_entry_count = 17;
27 27
28 if (!IsControllerActivated()) { 28 if (!IsControllerActivated()) {
@@ -49,7 +49,7 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
49 touch_entry.diameter_x = Settings::values.touchscreen.diameter_x; 49 touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
50 touch_entry.diameter_y = Settings::values.touchscreen.diameter_y; 50 touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
51 touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle; 51 touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle;
52 const u64 tick = core_timing.GetTicks(); 52 const u64 tick = core_timing.GetCPUTicks();
53 touch_entry.delta_time = tick - last_touch; 53 touch_entry.delta_time = tick - last_touch;
54 last_touch = tick; 54 last_touch = tick;
55 touch_entry.finger = Settings::values.touchscreen.finger; 55 touch_entry.finger = Settings::values.touchscreen.finger;
diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp
index 27511b27b..2503ef241 100644
--- a/src/core/hle/service/hid/controllers/xpad.cpp
+++ b/src/core/hle/service/hid/controllers/xpad.cpp
@@ -20,7 +20,7 @@ void Controller_XPad::OnRelease() {}
20void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, 20void Controller_XPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
21 std::size_t size) { 21 std::size_t size) {
22 for (auto& xpad_entry : shared_memory.shared_memory_entries) { 22 for (auto& xpad_entry : shared_memory.shared_memory_entries) {
23 xpad_entry.header.timestamp = core_timing.GetTicks(); 23 xpad_entry.header.timestamp = core_timing.GetCPUTicks();
24 xpad_entry.header.total_entry_count = 17; 24 xpad_entry.header.total_entry_count = 17;
25 25
26 if (!IsControllerActivated()) { 26 if (!IsControllerActivated()) {
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 57d5edea7..e9020e0dc 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -39,11 +39,9 @@ namespace Service::HID {
39 39
40// Updating period for each HID device. 40// Updating period for each HID device.
41// TODO(ogniK): Find actual polling rate of hid 41// TODO(ogniK): Find actual polling rate of hid
42constexpr s64 pad_update_ticks = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 66); 42constexpr s64 pad_update_ticks = static_cast<s64>(1000000000 / 66);
43[[maybe_unused]] constexpr s64 accelerometer_update_ticks = 43[[maybe_unused]] constexpr s64 accelerometer_update_ticks = static_cast<s64>(1000000000 / 100);
44 static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 100); 44[[maybe_unused]] constexpr s64 gyroscope_update_ticks = static_cast<s64>(1000000000 / 100);
45[[maybe_unused]] constexpr s64 gyroscope_update_ticks =
46 static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 100);
47constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; 45constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
48 46
49IAppletResource::IAppletResource(Core::System& system) 47IAppletResource::IAppletResource(Core::System& system)
@@ -78,8 +76,8 @@ IAppletResource::IAppletResource(Core::System& system)
78 76
79 // Register update callbacks 77 // Register update callbacks
80 pad_update_event = 78 pad_update_event =
81 Core::Timing::CreateEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 cycles_late) { 79 Core::Timing::CreateEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 ns_late) {
82 UpdateControllers(userdata, cycles_late); 80 UpdateControllers(userdata, ns_late);
83 }); 81 });
84 82
85 // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?) 83 // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?)
@@ -109,7 +107,7 @@ void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
109 rb.PushCopyObjects(shared_mem); 107 rb.PushCopyObjects(shared_mem);
110} 108}
111 109
112void IAppletResource::UpdateControllers(u64 userdata, s64 cycles_late) { 110void IAppletResource::UpdateControllers(u64 userdata, s64 ns_late) {
113 auto& core_timing = system.CoreTiming(); 111 auto& core_timing = system.CoreTiming();
114 112
115 const bool should_reload = Settings::values.is_device_reload_pending.exchange(false); 113 const bool should_reload = Settings::values.is_device_reload_pending.exchange(false);
@@ -120,7 +118,7 @@ void IAppletResource::UpdateControllers(u64 userdata, s64 cycles_late) {
120 controller->OnUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE); 118 controller->OnUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE);
121 } 119 }
122 120
123 core_timing.ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event); 121 core_timing.ScheduleEvent(pad_update_ticks - ns_late, pad_update_event);
124} 122}
125 123
126class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { 124class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp
index 36ed6f7da..e82fd031b 100644
--- a/src/core/hle/service/hid/irs.cpp
+++ b/src/core/hle/service/hid/irs.cpp
@@ -98,7 +98,7 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
98 98
99 IPC::ResponseBuilder rb{ctx, 5}; 99 IPC::ResponseBuilder rb{ctx, 5};
100 rb.Push(RESULT_SUCCESS); 100 rb.Push(RESULT_SUCCESS);
101 rb.PushRaw<u64>(system.CoreTiming().GetTicks()); 101 rb.PushRaw<u64>(system.CoreTiming().GetCPUTicks());
102 rb.PushRaw<u32>(0); 102 rb.PushRaw<u32>(0);
103} 103}
104 104
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index 0d913334e..fba89e7a6 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -200,8 +200,7 @@ u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& o
200 200
201 IoctlGetGpuTime params{}; 201 IoctlGetGpuTime params{};
202 std::memcpy(&params, input.data(), input.size()); 202 std::memcpy(&params, input.data(), input.size());
203 const auto ns = Core::Timing::CyclesToNs(system.CoreTiming().GetTicks()); 203 params.gpu_time = static_cast<u64_le>(system.CoreTiming().GetGlobalTimeNs().count());
204 params.gpu_time = static_cast<u64_le>(ns.count());
205 std::memcpy(output.data(), &params, output.size()); 204 std::memcpy(output.data(), &params, output.size());
206 return 0; 205 return 0;
207} 206}
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 437bc5dee..aaf28995d 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -27,8 +27,8 @@
27 27
28namespace Service::NVFlinger { 28namespace Service::NVFlinger {
29 29
30constexpr s64 frame_ticks = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 60); 30constexpr s64 frame_ticks = static_cast<s64>(1000000000 / 60);
31constexpr s64 frame_ticks_30fps = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 30); 31constexpr s64 frame_ticks_30fps = static_cast<s64>(1000000000 / 30);
32 32
33NVFlinger::NVFlinger(Core::System& system) : system(system) { 33NVFlinger::NVFlinger(Core::System& system) : system(system) {
34 displays.emplace_back(0, "Default", system); 34 displays.emplace_back(0, "Default", system);
@@ -39,11 +39,10 @@ NVFlinger::NVFlinger(Core::System& system) : system(system) {
39 39
40 // Schedule the screen composition events 40 // Schedule the screen composition events
41 composition_event = 41 composition_event =
42 Core::Timing::CreateEvent("ScreenComposition", [this](u64 userdata, s64 cycles_late) { 42 Core::Timing::CreateEvent("ScreenComposition", [this](u64 userdata, s64 ns_late) {
43 Compose(); 43 Compose();
44 const auto ticks = 44 const auto ticks = GetNextTicks();
45 Settings::values.force_30fps_mode ? frame_ticks_30fps : GetNextTicks(); 45 this->system.CoreTiming().ScheduleEvent(std::max<s64>(0LL, ticks - ns_late),
46 this->system.CoreTiming().ScheduleEvent(std::max<s64>(0LL, ticks - cycles_late),
47 composition_event); 46 composition_event);
48 }); 47 });
49 48
@@ -223,7 +222,7 @@ void NVFlinger::Compose() {
223 222
224s64 NVFlinger::GetNextTicks() const { 223s64 NVFlinger::GetNextTicks() const {
225 constexpr s64 max_hertz = 120LL; 224 constexpr s64 max_hertz = 120LL;
226 return (Core::Hardware::BASE_CLOCK_RATE * (1LL << swap_interval)) / max_hertz; 225 return (1000000000 * (1LL << swap_interval)) / max_hertz;
227} 226}
228 227
229} // namespace Service::NVFlinger 228} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/time/standard_steady_clock_core.cpp b/src/core/hle/service/time/standard_steady_clock_core.cpp
index 1575f0b49..59a272f4a 100644
--- a/src/core/hle/service/time/standard_steady_clock_core.cpp
+++ b/src/core/hle/service/time/standard_steady_clock_core.cpp
@@ -11,9 +11,8 @@
11namespace Service::Time::Clock { 11namespace Service::Time::Clock {
12 12
13TimeSpanType StandardSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) { 13TimeSpanType StandardSteadyClockCore::GetCurrentRawTimePoint(Core::System& system) {
14 const TimeSpanType ticks_time_span{TimeSpanType::FromTicks( 14 const TimeSpanType ticks_time_span{
15 Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()), 15 TimeSpanType::FromTicks(system.CoreTiming().GetClockTicks(), Core::Hardware::CNTFREQ)};
16 Core::Hardware::CNTFREQ)};
17 TimeSpanType raw_time_point{setup_value.nanoseconds + ticks_time_span.nanoseconds}; 16 TimeSpanType raw_time_point{setup_value.nanoseconds + ticks_time_span.nanoseconds};
18 17
19 if (raw_time_point.nanoseconds < cached_raw_time_point.nanoseconds) { 18 if (raw_time_point.nanoseconds < cached_raw_time_point.nanoseconds) {
diff --git a/src/core/hle/service/time/tick_based_steady_clock_core.cpp b/src/core/hle/service/time/tick_based_steady_clock_core.cpp
index 44d5bc651..8baaa2a6a 100644
--- a/src/core/hle/service/time/tick_based_steady_clock_core.cpp
+++ b/src/core/hle/service/time/tick_based_steady_clock_core.cpp
@@ -11,9 +11,8 @@
11namespace Service::Time::Clock { 11namespace Service::Time::Clock {
12 12
13SteadyClockTimePoint TickBasedSteadyClockCore::GetTimePoint(Core::System& system) { 13SteadyClockTimePoint TickBasedSteadyClockCore::GetTimePoint(Core::System& system) {
14 const TimeSpanType ticks_time_span{TimeSpanType::FromTicks( 14 const TimeSpanType ticks_time_span{
15 Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()), 15 TimeSpanType::FromTicks(system.CoreTiming().GetClockTicks(), Core::Hardware::CNTFREQ)};
16 Core::Hardware::CNTFREQ)};
17 16
18 return {ticks_time_span.ToSeconds(), GetClockSourceId()}; 17 return {ticks_time_span.ToSeconds(), GetClockSourceId()};
19} 18}
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index 67f1bbcf3..4cf58a61a 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -234,9 +234,8 @@ void Module::Interface::CalculateMonotonicSystemClockBaseTimePoint(Kernel::HLERe
234 const auto current_time_point{steady_clock_core.GetCurrentTimePoint(system)}; 234 const auto current_time_point{steady_clock_core.GetCurrentTimePoint(system)};
235 235
236 if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) { 236 if (current_time_point.clock_source_id == context.steady_time_point.clock_source_id) {
237 const auto ticks{Clock::TimeSpanType::FromTicks( 237 const auto ticks{Clock::TimeSpanType::FromTicks(system.CoreTiming().GetClockTicks(),
238 Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()), 238 Core::Hardware::CNTFREQ)};
239 Core::Hardware::CNTFREQ)};
240 const s64 base_time_point{context.offset + current_time_point.time_point - 239 const s64 base_time_point{context.offset + current_time_point.time_point -
241 ticks.ToSeconds()}; 240 ticks.ToSeconds()};
242 IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2}; 241 IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2};
diff --git a/src/core/hle/service/time/time_sharedmemory.cpp b/src/core/hle/service/time/time_sharedmemory.cpp
index 999ec1e51..e0ae9f874 100644
--- a/src/core/hle/service/time/time_sharedmemory.cpp
+++ b/src/core/hle/service/time/time_sharedmemory.cpp
@@ -30,8 +30,7 @@ void SharedMemory::SetupStandardSteadyClock(Core::System& system,
30 const Common::UUID& clock_source_id, 30 const Common::UUID& clock_source_id,
31 Clock::TimeSpanType current_time_point) { 31 Clock::TimeSpanType current_time_point) {
32 const Clock::TimeSpanType ticks_time_span{Clock::TimeSpanType::FromTicks( 32 const Clock::TimeSpanType ticks_time_span{Clock::TimeSpanType::FromTicks(
33 Core::Timing::CpuCyclesToClockCycles(system.CoreTiming().GetTicks()), 33 system.CoreTiming().GetClockTicks(), Core::Hardware::CNTFREQ)};
34 Core::Hardware::CNTFREQ)};
35 const Clock::SteadyClockContext context{ 34 const Clock::SteadyClockContext context{
36 static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds), 35 static_cast<u64>(current_time_point.nanoseconds - ticks_time_span.nanoseconds),
37 clock_source_id}; 36 clock_source_id};
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 9d87045a0..66634596d 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -29,15 +29,12 @@ namespace Core::Memory {
29struct Memory::Impl { 29struct Memory::Impl {
30 explicit Impl(Core::System& system_) : system{system_} {} 30 explicit Impl(Core::System& system_) : system{system_} {}
31 31
32 void SetCurrentPageTable(Kernel::Process& process) { 32 void SetCurrentPageTable(Kernel::Process& process, u32 core_id) {
33 current_page_table = &process.PageTable().PageTableImpl(); 33 current_page_table = &process.PageTable().PageTableImpl();
34 34
35 const std::size_t address_space_width = process.PageTable().GetAddressSpaceWidth(); 35 const std::size_t address_space_width = process.PageTable().GetAddressSpaceWidth();
36 36
37 system.ArmInterface(0).PageTableChanged(*current_page_table, address_space_width); 37 system.ArmInterface(core_id).PageTableChanged(*current_page_table, address_space_width);
38 system.ArmInterface(1).PageTableChanged(*current_page_table, address_space_width);
39 system.ArmInterface(2).PageTableChanged(*current_page_table, address_space_width);
40 system.ArmInterface(3).PageTableChanged(*current_page_table, address_space_width);
41 } 38 }
42 39
43 void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) { 40 void MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) {
@@ -689,8 +686,8 @@ struct Memory::Impl {
689Memory::Memory(Core::System& system) : impl{std::make_unique<Impl>(system)} {} 686Memory::Memory(Core::System& system) : impl{std::make_unique<Impl>(system)} {}
690Memory::~Memory() = default; 687Memory::~Memory() = default;
691 688
692void Memory::SetCurrentPageTable(Kernel::Process& process) { 689void Memory::SetCurrentPageTable(Kernel::Process& process, u32 core_id) {
693 impl->SetCurrentPageTable(process); 690 impl->SetCurrentPageTable(process, core_id);
694} 691}
695 692
696void Memory::MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) { 693void Memory::MapMemoryRegion(Common::PageTable& page_table, VAddr base, u64 size, PAddr target) {
diff --git a/src/core/memory.h b/src/core/memory.h
index 9292f3b0a..93f0c1d6c 100644
--- a/src/core/memory.h
+++ b/src/core/memory.h
@@ -64,7 +64,7 @@ public:
64 * 64 *
65 * @param process The process to use the page table of. 65 * @param process The process to use the page table of.
66 */ 66 */
67 void SetCurrentPageTable(Kernel::Process& process); 67 void SetCurrentPageTable(Kernel::Process& process, u32 core_id);
68 68
69 /** 69 /**
70 * Maps an allocated buffer onto a region of the emulated process address space. 70 * Maps an allocated buffer onto a region of the emulated process address space.
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index b139e8465..53d27859b 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -20,7 +20,7 @@
20 20
21namespace Core::Memory { 21namespace Core::Memory {
22 22
23constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 12); 23constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(1000000000 / 12);
24constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF; 24constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF;
25 25
26StandardVmCallbacks::StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata) 26StandardVmCallbacks::StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata)
@@ -190,7 +190,7 @@ CheatEngine::~CheatEngine() {
190void CheatEngine::Initialize() { 190void CheatEngine::Initialize() {
191 event = Core::Timing::CreateEvent( 191 event = Core::Timing::CreateEvent(
192 "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id), 192 "CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id),
193 [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); }); 193 [this](u64 userdata, s64 ns_late) { FrameCallback(userdata, ns_late); });
194 core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event); 194 core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event);
195 195
196 metadata.process_id = system.CurrentProcess()->GetProcessID(); 196 metadata.process_id = system.CurrentProcess()->GetProcessID();
@@ -217,7 +217,7 @@ void CheatEngine::Reload(std::vector<CheatEntry> cheats) {
217 217
218MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70)); 218MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70));
219 219
220void CheatEngine::FrameCallback(u64 userdata, s64 cycles_late) { 220void CheatEngine::FrameCallback(u64 userdata, s64 ns_late) {
221 if (is_pending_reload.exchange(false)) { 221 if (is_pending_reload.exchange(false)) {
222 vm.LoadProgram(cheats); 222 vm.LoadProgram(cheats);
223 } 223 }
@@ -230,7 +230,7 @@ void CheatEngine::FrameCallback(u64 userdata, s64 cycles_late) {
230 230
231 vm.Execute(metadata); 231 vm.Execute(metadata);
232 232
233 core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - cycles_late, event); 233 core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - ns_late, event);
234} 234}
235 235
236} // namespace Core::Memory 236} // namespace Core::Memory
diff --git a/src/core/tools/freezer.cpp b/src/core/tools/freezer.cpp
index b2c6c537e..8b0c50d11 100644
--- a/src/core/tools/freezer.cpp
+++ b/src/core/tools/freezer.cpp
@@ -14,7 +14,7 @@
14namespace Tools { 14namespace Tools {
15namespace { 15namespace {
16 16
17constexpr s64 MEMORY_FREEZER_TICKS = static_cast<s64>(Core::Hardware::BASE_CLOCK_RATE / 60); 17constexpr s64 MEMORY_FREEZER_TICKS = static_cast<s64>(1000000000 / 60);
18 18
19u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) { 19u64 MemoryReadWidth(Core::Memory::Memory& memory, u32 width, VAddr addr) {
20 switch (width) { 20 switch (width) {
@@ -57,7 +57,7 @@ Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& m
57 : core_timing{core_timing_}, memory{memory_} { 57 : core_timing{core_timing_}, memory{memory_} {
58 event = Core::Timing::CreateEvent( 58 event = Core::Timing::CreateEvent(
59 "MemoryFreezer::FrameCallback", 59 "MemoryFreezer::FrameCallback",
60 [this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); }); 60 [this](u64 userdata, s64 ns_late) { FrameCallback(userdata, ns_late); });
61 core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS, event); 61 core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS, event);
62} 62}
63 63
@@ -158,7 +158,7 @@ std::vector<Freezer::Entry> Freezer::GetEntries() const {
158 return entries; 158 return entries;
159} 159}
160 160
161void Freezer::FrameCallback(u64 userdata, s64 cycles_late) { 161void Freezer::FrameCallback(u64 userdata, s64 ns_late) {
162 if (!IsActive()) { 162 if (!IsActive()) {
163 LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events."); 163 LOG_DEBUG(Common_Memory, "Memory freezer has been deactivated, ending callback events.");
164 return; 164 return;
@@ -173,7 +173,7 @@ void Freezer::FrameCallback(u64 userdata, s64 cycles_late) {
173 MemoryWriteWidth(memory, entry.width, entry.address, entry.value); 173 MemoryWriteWidth(memory, entry.width, entry.address, entry.value);
174 } 174 }
175 175
176 core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS - cycles_late, event); 176 core_timing.ScheduleEvent(MEMORY_FREEZER_TICKS - ns_late, event);
177} 177}
178 178
179void Freezer::FillEntryReads() { 179void Freezer::FillEntryReads() {
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 3f750b51c..47ef30aa9 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -8,7 +8,6 @@ add_executable(tests
8 core/arm/arm_test_common.cpp 8 core/arm/arm_test_common.cpp
9 core/arm/arm_test_common.h 9 core/arm/arm_test_common.h
10 core/core_timing.cpp 10 core/core_timing.cpp
11 core/host_timing.cpp
12 tests.cpp 11 tests.cpp
13) 12)
14 13
diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp
index ff2d11cc8..795f3da09 100644
--- a/src/tests/core/core_timing.cpp
+++ b/src/tests/core/core_timing.cpp
@@ -16,31 +16,30 @@
16 16
17namespace { 17namespace {
18// Numbers are chosen randomly to make sure the correct one is given. 18// Numbers are chosen randomly to make sure the correct one is given.
19constexpr std::array<u64, 5> CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}}; 19static constexpr std::array<u64, 5> CB_IDS{{42, 144, 93, 1026, UINT64_C(0xFFFF7FFFF7FFFF)}};
20constexpr int MAX_SLICE_LENGTH = 10000; // Copied from CoreTiming internals 20static constexpr int MAX_SLICE_LENGTH = 10000; // Copied from CoreTiming internals
21static constexpr std::array<u64, 5> calls_order{{2, 0, 1, 4, 3}};
22static std::array<s64, 5> delays{};
21 23
22std::bitset<CB_IDS.size()> callbacks_ran_flags; 24std::bitset<CB_IDS.size()> callbacks_ran_flags;
23u64 expected_callback = 0; 25u64 expected_callback = 0;
24s64 lateness = 0; 26s64 lateness = 0;
25 27
26template <unsigned int IDX> 28template <unsigned int IDX>
27void CallbackTemplate(u64 userdata, s64 cycles_late) { 29void HostCallbackTemplate(u64 userdata, s64 nanoseconds_late) {
28 static_assert(IDX < CB_IDS.size(), "IDX out of range"); 30 static_assert(IDX < CB_IDS.size(), "IDX out of range");
29 callbacks_ran_flags.set(IDX); 31 callbacks_ran_flags.set(IDX);
30 REQUIRE(CB_IDS[IDX] == userdata); 32 REQUIRE(CB_IDS[IDX] == userdata);
31 REQUIRE(CB_IDS[IDX] == expected_callback); 33 REQUIRE(CB_IDS[IDX] == CB_IDS[calls_order[expected_callback]]);
32 REQUIRE(lateness == cycles_late); 34 delays[IDX] = nanoseconds_late;
35 ++expected_callback;
33} 36}
34 37
35u64 callbacks_done = 0; 38u64 callbacks_done = 0;
36 39
37void EmptyCallback(u64 userdata, s64 cycles_late) {
38 ++callbacks_done;
39}
40
41struct ScopeInit final { 40struct ScopeInit final {
42 ScopeInit() { 41 ScopeInit() {
43 core_timing.Initialize(); 42 core_timing.Initialize([]() {});
44 } 43 }
45 ~ScopeInit() { 44 ~ScopeInit() {
46 core_timing.Shutdown(); 45 core_timing.Shutdown();
@@ -49,110 +48,97 @@ struct ScopeInit final {
49 Core::Timing::CoreTiming core_timing; 48 Core::Timing::CoreTiming core_timing;
50}; 49};
51 50
52void AdvanceAndCheck(Core::Timing::CoreTiming& core_timing, u32 idx, u32 context = 0,
53 int expected_lateness = 0, int cpu_downcount = 0) {
54 callbacks_ran_flags = 0;
55 expected_callback = CB_IDS[idx];
56 lateness = expected_lateness;
57
58 // Pretend we executed X cycles of instructions.
59 core_timing.SwitchContext(context);
60 core_timing.AddTicks(core_timing.GetDowncount() - cpu_downcount);
61 core_timing.Advance();
62 core_timing.SwitchContext((context + 1) % 4);
63
64 REQUIRE(decltype(callbacks_ran_flags)().set(idx) == callbacks_ran_flags);
65}
66} // Anonymous namespace
67
68TEST_CASE("CoreTiming[BasicOrder]", "[core]") { 51TEST_CASE("CoreTiming[BasicOrder]", "[core]") {
69 ScopeInit guard; 52 ScopeInit guard;
70 auto& core_timing = guard.core_timing; 53 auto& core_timing = guard.core_timing;
54 std::vector<std::shared_ptr<Core::Timing::EventType>> events{
55 Core::Timing::CreateEvent("callbackA", HostCallbackTemplate<0>),
56 Core::Timing::CreateEvent("callbackB", HostCallbackTemplate<1>),
57 Core::Timing::CreateEvent("callbackC", HostCallbackTemplate<2>),
58 Core::Timing::CreateEvent("callbackD", HostCallbackTemplate<3>),
59 Core::Timing::CreateEvent("callbackE", HostCallbackTemplate<4>),
60 };
61
62 expected_callback = 0;
63
64 core_timing.SyncPause(true);
65
66 u64 one_micro = 1000U;
67 for (std::size_t i = 0; i < events.size(); i++) {
68 u64 order = calls_order[i];
69 core_timing.ScheduleEvent(i * one_micro + 100U, events[order], CB_IDS[order]);
70 }
71 /// test pause
72 REQUIRE(callbacks_ran_flags.none());
71 73
72 std::shared_ptr<Core::Timing::EventType> cb_a = 74 core_timing.Pause(false); // No need to sync
73 Core::Timing::CreateEvent("callbackA", CallbackTemplate<0>);
74 std::shared_ptr<Core::Timing::EventType> cb_b =
75 Core::Timing::CreateEvent("callbackB", CallbackTemplate<1>);
76 std::shared_ptr<Core::Timing::EventType> cb_c =
77 Core::Timing::CreateEvent("callbackC", CallbackTemplate<2>);
78 std::shared_ptr<Core::Timing::EventType> cb_d =
79 Core::Timing::CreateEvent("callbackD", CallbackTemplate<3>);
80 std::shared_ptr<Core::Timing::EventType> cb_e =
81 Core::Timing::CreateEvent("callbackE", CallbackTemplate<4>);
82
83 // Enter slice 0
84 core_timing.ResetRun();
85
86 // D -> B -> C -> A -> E
87 core_timing.SwitchContext(0);
88 core_timing.ScheduleEvent(1000, cb_a, CB_IDS[0]);
89 REQUIRE(1000 == core_timing.GetDowncount());
90 core_timing.ScheduleEvent(500, cb_b, CB_IDS[1]);
91 REQUIRE(500 == core_timing.GetDowncount());
92 core_timing.ScheduleEvent(800, cb_c, CB_IDS[2]);
93 REQUIRE(500 == core_timing.GetDowncount());
94 core_timing.ScheduleEvent(100, cb_d, CB_IDS[3]);
95 REQUIRE(100 == core_timing.GetDowncount());
96 core_timing.ScheduleEvent(1200, cb_e, CB_IDS[4]);
97 REQUIRE(100 == core_timing.GetDowncount());
98
99 AdvanceAndCheck(core_timing, 3, 0);
100 AdvanceAndCheck(core_timing, 1, 1);
101 AdvanceAndCheck(core_timing, 2, 2);
102 AdvanceAndCheck(core_timing, 0, 3);
103 AdvanceAndCheck(core_timing, 4, 0);
104}
105
106TEST_CASE("CoreTiming[FairSharing]", "[core]") {
107 75
108 ScopeInit guard; 76 while (core_timing.HasPendingEvents())
109 auto& core_timing = guard.core_timing; 77 ;
110 78
111 std::shared_ptr<Core::Timing::EventType> empty_callback = 79 REQUIRE(callbacks_ran_flags.all());
112 Core::Timing::CreateEvent("empty_callback", EmptyCallback);
113 80
114 callbacks_done = 0; 81 for (std::size_t i = 0; i < delays.size(); i++) {
115 u64 MAX_CALLBACKS = 10; 82 const double delay = static_cast<double>(delays[i]);
116 for (std::size_t i = 0; i < 10; i++) { 83 const double micro = delay / 1000.0f;
117 core_timing.ScheduleEvent(i * 3333U, empty_callback, 0); 84 const double mili = micro / 1000.0f;
85 printf("HostTimer Pausing Delay[%zu]: %.3f %.6f\n", i, micro, mili);
118 } 86 }
87}
119 88
120 const s64 advances = MAX_SLICE_LENGTH / 10; 89#pragma optimize("", off)
121 core_timing.ResetRun(); 90u64 TestTimerSpeed(Core::Timing::CoreTiming& core_timing) {
122 u64 current_time = core_timing.GetTicks(); 91 u64 start = core_timing.GetGlobalTimeNs().count();
123 bool keep_running{}; 92 u64 placebo = 0;
124 do { 93 for (std::size_t i = 0; i < 1000; i++) {
125 keep_running = false; 94 placebo += core_timing.GetGlobalTimeNs().count();
126 for (u32 active_core = 0; active_core < 4; ++active_core) { 95 }
127 core_timing.SwitchContext(active_core); 96 u64 end = core_timing.GetGlobalTimeNs().count();
128 if (core_timing.CanCurrentContextRun()) { 97 return (end - start);
129 core_timing.AddTicks(std::min<s64>(advances, core_timing.GetDowncount()));
130 core_timing.Advance();
131 }
132 keep_running |= core_timing.CanCurrentContextRun();
133 }
134 } while (keep_running);
135 u64 current_time_2 = core_timing.GetTicks();
136
137 REQUIRE(MAX_CALLBACKS == callbacks_done);
138 REQUIRE(current_time_2 == current_time + MAX_SLICE_LENGTH * 4);
139} 98}
99#pragma optimize("", on)
140 100
141TEST_CASE("Core::Timing[PredictableLateness]", "[core]") { 101TEST_CASE("CoreTiming[BasicOrderNoPausing]", "[core]") {
142 ScopeInit guard; 102 ScopeInit guard;
143 auto& core_timing = guard.core_timing; 103 auto& core_timing = guard.core_timing;
104 std::vector<std::shared_ptr<Core::Timing::EventType>> events{
105 Core::Timing::CreateEvent("callbackA", HostCallbackTemplate<0>),
106 Core::Timing::CreateEvent("callbackB", HostCallbackTemplate<1>),
107 Core::Timing::CreateEvent("callbackC", HostCallbackTemplate<2>),
108 Core::Timing::CreateEvent("callbackD", HostCallbackTemplate<3>),
109 Core::Timing::CreateEvent("callbackE", HostCallbackTemplate<4>),
110 };
111
112 core_timing.SyncPause(true);
113 core_timing.SyncPause(false);
114
115 expected_callback = 0;
116
117 u64 start = core_timing.GetGlobalTimeNs().count();
118 u64 one_micro = 1000U;
119 for (std::size_t i = 0; i < events.size(); i++) {
120 u64 order = calls_order[i];
121 core_timing.ScheduleEvent(i * one_micro + 100U, events[order], CB_IDS[order]);
122 }
123 u64 end = core_timing.GetGlobalTimeNs().count();
124 const double scheduling_time = static_cast<double>(end - start);
125 const double timer_time = static_cast<double>(TestTimerSpeed(core_timing));
144 126
145 std::shared_ptr<Core::Timing::EventType> cb_a = 127 while (core_timing.HasPendingEvents())
146 Core::Timing::CreateEvent("callbackA", CallbackTemplate<0>); 128 ;
147 std::shared_ptr<Core::Timing::EventType> cb_b =
148 Core::Timing::CreateEvent("callbackB", CallbackTemplate<1>);
149 129
150 // Enter slice 0 130 REQUIRE(callbacks_ran_flags.all());
151 core_timing.ResetRun();
152 131
153 core_timing.ScheduleEvent(100, cb_a, CB_IDS[0]); 132 for (std::size_t i = 0; i < delays.size(); i++) {
154 core_timing.ScheduleEvent(200, cb_b, CB_IDS[1]); 133 const double delay = static_cast<double>(delays[i]);
134 const double micro = delay / 1000.0f;
135 const double mili = micro / 1000.0f;
136 printf("HostTimer No Pausing Delay[%zu]: %.3f %.6f\n", i, micro, mili);
137 }
155 138
156 AdvanceAndCheck(core_timing, 0, 0, 10, -10); // (100 - 10) 139 const double micro = scheduling_time / 1000.0f;
157 AdvanceAndCheck(core_timing, 1, 1, 50, -50); 140 const double mili = micro / 1000.0f;
141 printf("HostTimer No Pausing Scheduling Time: %.3f %.6f\n", micro, mili);
142 printf("HostTimer No Pausing Timer Time: %.3f %.6f\n", timer_time / 1000.f,
143 timer_time / 1000000.f);
158} 144}
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 8eb017f65..482e49711 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -2,6 +2,8 @@
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 <chrono>
6
5#include "common/assert.h" 7#include "common/assert.h"
6#include "common/microprofile.h" 8#include "common/microprofile.h"
7#include "core/core.h" 9#include "core/core.h"
@@ -154,8 +156,7 @@ u64 GPU::GetTicks() const {
154 constexpr u64 gpu_ticks_num = 384; 156 constexpr u64 gpu_ticks_num = 384;
155 constexpr u64 gpu_ticks_den = 625; 157 constexpr u64 gpu_ticks_den = 625;
156 158
157 const u64 cpu_ticks = system.CoreTiming().GetTicks(); 159 u64 nanoseconds = system.CoreTiming().GetGlobalTimeNs().count();
158 u64 nanoseconds = Core::Timing::CyclesToNs(cpu_ticks).count();
159 if (Settings::values.use_fast_gpu_time) { 160 if (Settings::values.use_fast_gpu_time) {
160 nanoseconds /= 256; 161 nanoseconds /= 256;
161 } 162 }
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index bfeb16458..9ceb6c8d7 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -52,6 +52,8 @@ void EmuThread::run() {
52 52
53 emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); 53 emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
54 54
55 Core::System::GetInstance().RegisterHostThread();
56
55 Core::System::GetInstance().Renderer().Rasterizer().LoadDiskResources( 57 Core::System::GetInstance().Renderer().Rasterizer().LoadDiskResources(
56 stop_run, [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) { 58 stop_run, [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) {
57 emit LoadProgress(stage, value, total); 59 emit LoadProgress(stage, value, total);
@@ -65,28 +67,30 @@ void EmuThread::run() {
65 bool was_active = false; 67 bool was_active = false;
66 while (!stop_run) { 68 while (!stop_run) {
67 if (running) { 69 if (running) {
68 if (!was_active) 70 if (was_active) {
69 emit DebugModeLeft(); 71 emit DebugModeLeft();
72 }
70 73
71 Core::System::ResultStatus result = Core::System::GetInstance().RunLoop(); 74 running_guard = true;
75 Core::System::ResultStatus result = Core::System::GetInstance().Run();
72 if (result != Core::System::ResultStatus::Success) { 76 if (result != Core::System::ResultStatus::Success) {
77 running_guard = false;
73 this->SetRunning(false); 78 this->SetRunning(false);
74 emit ErrorThrown(result, Core::System::GetInstance().GetStatusDetails()); 79 emit ErrorThrown(result, Core::System::GetInstance().GetStatusDetails());
75 } 80 }
81 running_wait.Wait();
82 result = Core::System::GetInstance().Pause();
83 if (result != Core::System::ResultStatus::Success) {
84 running_guard = false;
85 this->SetRunning(false);
86 emit ErrorThrown(result, Core::System::GetInstance().GetStatusDetails());
87 }
88 running_guard = false;
76 89
77 was_active = running || exec_step; 90 was_active = true;
78 if (!was_active && !stop_run)
79 emit DebugModeEntered();
80 } else if (exec_step) {
81 if (!was_active)
82 emit DebugModeLeft();
83
84 exec_step = false;
85 Core::System::GetInstance().SingleStep();
86 emit DebugModeEntered(); 91 emit DebugModeEntered();
87 yieldCurrentThread(); 92 } else if (exec_step) {
88 93 UNIMPLEMENTED();
89 was_active = false;
90 } else { 94 } else {
91 std::unique_lock lock{running_mutex}; 95 std::unique_lock lock{running_mutex};
92 running_cv.wait(lock, [this] { return IsRunning() || exec_step || stop_run; }); 96 running_cv.wait(lock, [this] { return IsRunning() || exec_step || stop_run; });
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 3626604ca..768568b3e 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -59,6 +59,11 @@ public:
59 this->running = running; 59 this->running = running;
60 lock.unlock(); 60 lock.unlock();
61 running_cv.notify_all(); 61 running_cv.notify_all();
62 if (!running) {
63 running_wait.Set();
64 /// Wait until effectively paused
65 while (running_guard);
66 }
62 } 67 }
63 68
64 /** 69 /**
@@ -84,6 +89,8 @@ private:
84 std::atomic_bool stop_run{false}; 89 std::atomic_bool stop_run{false};
85 std::mutex running_mutex; 90 std::mutex running_mutex;
86 std::condition_variable running_cv; 91 std::condition_variable running_cv;
92 Common::Event running_wait{};
93 std::atomic_bool running_guard{false};
87 94
88signals: 95signals:
89 /** 96 /**
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp
index c1ea25fb8..765908c5a 100644
--- a/src/yuzu/debugger/wait_tree.cpp
+++ b/src/yuzu/debugger/wait_tree.cpp
@@ -59,8 +59,10 @@ std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList()
59 std::size_t row = 0; 59 std::size_t row = 0;
60 auto add_threads = [&](const std::vector<std::shared_ptr<Kernel::Thread>>& threads) { 60 auto add_threads = [&](const std::vector<std::shared_ptr<Kernel::Thread>>& threads) {
61 for (std::size_t i = 0; i < threads.size(); ++i) { 61 for (std::size_t i = 0; i < threads.size(); ++i) {
62 item_list.push_back(std::make_unique<WaitTreeThread>(*threads[i])); 62 if (!threads[i]->IsHLEThread()) {
63 item_list.back()->row = row; 63 item_list.push_back(std::make_unique<WaitTreeThread>(*threads[i]));
64 item_list.back()->row = row;
65 }
64 ++row; 66 ++row;
65 } 67 }
66 }; 68 };
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 4d2ea7e9e..1e5377840 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -237,7 +237,7 @@ int main(int argc, char** argv) {
237 237
238 std::thread render_thread([&emu_window] { emu_window->Present(); }); 238 std::thread render_thread([&emu_window] { emu_window->Present(); });
239 while (emu_window->IsOpen()) { 239 while (emu_window->IsOpen()) {
240 system.RunLoop(); 240 //system.RunLoop();
241 } 241 }
242 render_thread.join(); 242 render_thread.join();
243 243
diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp
index 676e70ebd..1a45506d4 100644
--- a/src/yuzu_tester/yuzu.cpp
+++ b/src/yuzu_tester/yuzu.cpp
@@ -256,7 +256,7 @@ int main(int argc, char** argv) {
256 system.Renderer().Rasterizer().LoadDiskResources(); 256 system.Renderer().Rasterizer().LoadDiskResources();
257 257
258 while (!finished) { 258 while (!finished) {
259 system.RunLoop(); 259 //system.RunLoop();
260 } 260 }
261 261
262 detached_tasks.WaitForAllTasks(); 262 detached_tasks.WaitForAllTasks();