cpu_manager.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. // Copyright 2018 yuzu emulator team
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include "common/fiber.h"
  5. #include "common/microprofile.h"
  6. #include "common/thread.h"
  7. #include "core/arm/exclusive_monitor.h"
  8. #include "core/core.h"
  9. #include "core/core_timing.h"
  10. #include "core/cpu_manager.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"
  16. #include "video_core/gpu.h"
  17. namespace Core {
  18. CpuManager::CpuManager(System& system) : system{system} {}
  19. CpuManager::~CpuManager() = default;
  20. void CpuManager::ThreadStart(CpuManager& cpu_manager, std::size_t core) {
  21. cpu_manager.RunThread(core);
  22. }
  23. void CpuManager::Initialize() {
  24. running_mode = true;
  25. if (is_multicore) {
  26. for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
  27. core_data[core].host_thread =
  28. std::make_unique<std::thread>(ThreadStart, std::ref(*this), core);
  29. }
  30. } else {
  31. core_data[0].host_thread = std::make_unique<std::thread>(ThreadStart, std::ref(*this), 0);
  32. }
  33. }
  34. void CpuManager::Shutdown() {
  35. running_mode = false;
  36. Pause(false);
  37. if (is_multicore) {
  38. for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
  39. core_data[core].host_thread->join();
  40. core_data[core].host_thread.reset();
  41. }
  42. } else {
  43. core_data[0].host_thread->join();
  44. core_data[0].host_thread.reset();
  45. }
  46. }
  47. std::function<void(void*)> CpuManager::GetGuestThreadStartFunc() {
  48. return std::function<void(void*)>(GuestThreadFunction);
  49. }
  50. std::function<void(void*)> CpuManager::GetIdleThreadStartFunc() {
  51. return std::function<void(void*)>(IdleThreadFunction);
  52. }
  53. std::function<void(void*)> CpuManager::GetSuspendThreadStartFunc() {
  54. return std::function<void(void*)>(SuspendThreadFunction);
  55. }
  56. void CpuManager::GuestThreadFunction(void* cpu_manager_) {
  57. CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
  58. if (cpu_manager->is_multicore) {
  59. cpu_manager->MultiCoreRunGuestThread();
  60. } else {
  61. cpu_manager->SingleCoreRunGuestThread();
  62. }
  63. }
  64. void CpuManager::GuestRewindFunction(void* cpu_manager_) {
  65. CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
  66. if (cpu_manager->is_multicore) {
  67. cpu_manager->MultiCoreRunGuestLoop();
  68. } else {
  69. cpu_manager->SingleCoreRunGuestLoop();
  70. }
  71. }
  72. void CpuManager::IdleThreadFunction(void* cpu_manager_) {
  73. CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
  74. if (cpu_manager->is_multicore) {
  75. cpu_manager->MultiCoreRunIdleThread();
  76. } else {
  77. cpu_manager->SingleCoreRunIdleThread();
  78. }
  79. }
  80. void CpuManager::SuspendThreadFunction(void* cpu_manager_) {
  81. CpuManager* cpu_manager = static_cast<CpuManager*>(cpu_manager_);
  82. if (cpu_manager->is_multicore) {
  83. cpu_manager->MultiCoreRunSuspendThread();
  84. } else {
  85. cpu_manager->SingleCoreRunSuspendThread();
  86. }
  87. }
  88. void* CpuManager::GetStartFuncParamater() {
  89. return static_cast<void*>(this);
  90. }
  91. ///////////////////////////////////////////////////////////////////////////////
  92. /// MultiCore ///
  93. ///////////////////////////////////////////////////////////////////////////////
  94. void CpuManager::MultiCoreRunGuestThread() {
  95. auto& kernel = system.Kernel();
  96. {
  97. auto& sched = kernel.CurrentScheduler();
  98. sched.OnThreadStart();
  99. }
  100. MultiCoreRunGuestLoop();
  101. }
  102. void CpuManager::MultiCoreRunGuestLoop() {
  103. auto& kernel = system.Kernel();
  104. auto* thread = kernel.CurrentScheduler().GetCurrentThread();
  105. while (true) {
  106. auto* physical_core = &kernel.CurrentPhysicalCore();
  107. auto& arm_interface = thread->ArmInterface();
  108. system.EnterDynarmicProfile();
  109. while (!physical_core->IsInterrupted()) {
  110. arm_interface.Run();
  111. physical_core = &kernel.CurrentPhysicalCore();
  112. }
  113. system.ExitDynarmicProfile();
  114. arm_interface.ClearExclusiveState();
  115. auto& scheduler = kernel.CurrentScheduler();
  116. scheduler.TryDoContextSwitch();
  117. }
  118. }
  119. void CpuManager::MultiCoreRunIdleThread() {
  120. auto& kernel = system.Kernel();
  121. while (true) {
  122. auto& physical_core = kernel.CurrentPhysicalCore();
  123. physical_core.Idle();
  124. auto& scheduler = kernel.CurrentScheduler();
  125. scheduler.TryDoContextSwitch();
  126. }
  127. }
  128. void CpuManager::MultiCoreRunSuspendThread() {
  129. auto& kernel = system.Kernel();
  130. {
  131. auto& sched = kernel.CurrentScheduler();
  132. sched.OnThreadStart();
  133. }
  134. while (true) {
  135. auto core = kernel.GetCurrentHostThreadID();
  136. auto& scheduler = kernel.CurrentScheduler();
  137. Kernel::Thread* current_thread = scheduler.GetCurrentThread();
  138. Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[core].host_context);
  139. ASSERT(scheduler.ContextSwitchPending());
  140. ASSERT(core == kernel.GetCurrentHostThreadID());
  141. scheduler.TryDoContextSwitch();
  142. }
  143. }
  144. void CpuManager::MultiCorePause(bool paused) {
  145. if (!paused) {
  146. bool all_not_barrier = false;
  147. while (!all_not_barrier) {
  148. all_not_barrier = true;
  149. for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
  150. all_not_barrier &=
  151. !core_data[core].is_running.load() && core_data[core].initialized.load();
  152. }
  153. }
  154. for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
  155. core_data[core].enter_barrier->Set();
  156. }
  157. if (paused_state.load()) {
  158. bool all_barrier = false;
  159. while (!all_barrier) {
  160. all_barrier = true;
  161. for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
  162. all_barrier &=
  163. core_data[core].is_paused.load() && core_data[core].initialized.load();
  164. }
  165. }
  166. for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
  167. core_data[core].exit_barrier->Set();
  168. }
  169. }
  170. } else {
  171. /// Wait until all cores are paused.
  172. bool all_barrier = false;
  173. while (!all_barrier) {
  174. all_barrier = true;
  175. for (std::size_t core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
  176. all_barrier &=
  177. core_data[core].is_paused.load() && core_data[core].initialized.load();
  178. }
  179. }
  180. /// Don't release the barrier
  181. }
  182. paused_state = paused;
  183. }
  184. ///////////////////////////////////////////////////////////////////////////////
  185. /// SingleCore ///
  186. ///////////////////////////////////////////////////////////////////////////////
  187. void CpuManager::SingleCoreRunGuestThread() {
  188. auto& kernel = system.Kernel();
  189. {
  190. auto& sched = kernel.CurrentScheduler();
  191. sched.OnThreadStart();
  192. }
  193. SingleCoreRunGuestLoop();
  194. }
  195. void CpuManager::SingleCoreRunGuestLoop() {
  196. auto& kernel = system.Kernel();
  197. auto* thread = kernel.CurrentScheduler().GetCurrentThread();
  198. while (true) {
  199. auto* physical_core = &kernel.CurrentPhysicalCore();
  200. auto& arm_interface = thread->ArmInterface();
  201. system.EnterDynarmicProfile();
  202. if (!physical_core->IsInterrupted()) {
  203. arm_interface.Run();
  204. physical_core = &kernel.CurrentPhysicalCore();
  205. }
  206. system.ExitDynarmicProfile();
  207. thread->SetPhantomMode(true);
  208. system.CoreTiming().Advance();
  209. thread->SetPhantomMode(false);
  210. arm_interface.ClearExclusiveState();
  211. PreemptSingleCore();
  212. auto& scheduler = kernel.Scheduler(current_core);
  213. scheduler.TryDoContextSwitch();
  214. }
  215. }
  216. void CpuManager::SingleCoreRunIdleThread() {
  217. auto& kernel = system.Kernel();
  218. while (true) {
  219. auto& physical_core = kernel.CurrentPhysicalCore();
  220. PreemptSingleCore(false);
  221. system.CoreTiming().AddTicks(1000U);
  222. idle_count++;
  223. auto& scheduler = physical_core.Scheduler();
  224. scheduler.TryDoContextSwitch();
  225. }
  226. }
  227. void CpuManager::SingleCoreRunSuspendThread() {
  228. auto& kernel = system.Kernel();
  229. {
  230. auto& sched = kernel.CurrentScheduler();
  231. sched.OnThreadStart();
  232. }
  233. while (true) {
  234. auto core = kernel.GetCurrentHostThreadID();
  235. auto& scheduler = kernel.CurrentScheduler();
  236. Kernel::Thread* current_thread = scheduler.GetCurrentThread();
  237. Common::Fiber::YieldTo(current_thread->GetHostContext(), core_data[0].host_context);
  238. ASSERT(scheduler.ContextSwitchPending());
  239. ASSERT(core == kernel.GetCurrentHostThreadID());
  240. scheduler.TryDoContextSwitch();
  241. }
  242. }
  243. void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
  244. std::size_t old_core = current_core;
  245. auto& scheduler = system.Kernel().Scheduler(old_core);
  246. Kernel::Thread* current_thread = scheduler.GetCurrentThread();
  247. if (idle_count >= 4 || from_running_enviroment) {
  248. if (!from_running_enviroment) {
  249. system.CoreTiming().Idle();
  250. idle_count = 0;
  251. }
  252. current_thread->SetPhantomMode(true);
  253. system.CoreTiming().Advance();
  254. current_thread->SetPhantomMode(false);
  255. }
  256. current_core.store((current_core + 1) % Core::Hardware::NUM_CPU_CORES);
  257. system.CoreTiming().ResetTicks();
  258. scheduler.Unload();
  259. auto& next_scheduler = system.Kernel().Scheduler(current_core);
  260. Common::Fiber::YieldTo(current_thread->GetHostContext(), next_scheduler.ControlContext());
  261. /// May have changed scheduler
  262. auto& current_scheduler = system.Kernel().Scheduler(current_core);
  263. current_scheduler.Reload();
  264. auto* currrent_thread2 = current_scheduler.GetCurrentThread();
  265. if (!currrent_thread2->IsIdleThread()) {
  266. idle_count = 0;
  267. }
  268. }
  269. void CpuManager::SingleCorePause(bool paused) {
  270. if (!paused) {
  271. bool all_not_barrier = false;
  272. while (!all_not_barrier) {
  273. all_not_barrier = !core_data[0].is_running.load() && core_data[0].initialized.load();
  274. }
  275. core_data[0].enter_barrier->Set();
  276. if (paused_state.load()) {
  277. bool all_barrier = false;
  278. while (!all_barrier) {
  279. all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load();
  280. }
  281. core_data[0].exit_barrier->Set();
  282. }
  283. } else {
  284. /// Wait until all cores are paused.
  285. bool all_barrier = false;
  286. while (!all_barrier) {
  287. all_barrier = core_data[0].is_paused.load() && core_data[0].initialized.load();
  288. }
  289. /// Don't release the barrier
  290. }
  291. paused_state = paused;
  292. }
  293. void CpuManager::Pause(bool paused) {
  294. if (is_multicore) {
  295. MultiCorePause(paused);
  296. } else {
  297. SingleCorePause(paused);
  298. }
  299. }
  300. void CpuManager::RunThread(std::size_t core) {
  301. /// Initialization
  302. system.RegisterCoreThread(core);
  303. std::string name;
  304. if (is_multicore) {
  305. name = "yuzu:CoreCPUThread_" + std::to_string(core);
  306. } else {
  307. name = "yuzu:CPUThread";
  308. }
  309. MicroProfileOnThreadCreate(name.c_str());
  310. Common::SetCurrentThreadName(name.c_str());
  311. Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
  312. auto& data = core_data[core];
  313. data.enter_barrier = std::make_unique<Common::Event>();
  314. data.exit_barrier = std::make_unique<Common::Event>();
  315. data.host_context = Common::Fiber::ThreadToFiber();
  316. data.is_running = false;
  317. data.initialized = true;
  318. const bool sc_sync = !is_async_gpu && !is_multicore;
  319. bool sc_sync_first_use = sc_sync;
  320. /// Running
  321. while (running_mode) {
  322. data.is_running = false;
  323. data.enter_barrier->Wait();
  324. if (sc_sync_first_use) {
  325. system.GPU().ObtainContext();
  326. sc_sync_first_use = false;
  327. }
  328. auto& scheduler = system.Kernel().CurrentScheduler();
  329. Kernel::Thread* current_thread = scheduler.GetCurrentThread();
  330. data.is_running = true;
  331. Common::Fiber::YieldTo(data.host_context, current_thread->GetHostContext());
  332. data.is_running = false;
  333. data.is_paused = true;
  334. data.exit_barrier->Wait();
  335. data.is_paused = false;
  336. }
  337. /// Time to cleanup
  338. data.host_context->Exit();
  339. data.enter_barrier.reset();
  340. data.exit_barrier.reset();
  341. data.initialized = false;
  342. }
  343. } // namespace Core