KScheduler.cs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. using Ryujinx.HLE.HOS.Kernel.Process;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. namespace Ryujinx.HLE.HOS.Kernel.Threading
  6. {
  7. partial class KScheduler : IDisposable
  8. {
  9. public const int PrioritiesCount = 64;
  10. public const int CpuCoresCount = 4;
  11. private const int PreemptionPriorityCores012 = 59;
  12. private const int PreemptionPriorityCore3 = 63;
  13. private Horizon _system;
  14. public KSchedulingData SchedulingData { get; private set; }
  15. public KCoreContext[] CoreContexts { get; private set; }
  16. public bool ThreadReselectionRequested { get; set; }
  17. public KScheduler(Horizon system)
  18. {
  19. _system = system;
  20. SchedulingData = new KSchedulingData();
  21. CoreManager = new HleCoreManager();
  22. CoreContexts = new KCoreContext[CpuCoresCount];
  23. for (int core = 0; core < CpuCoresCount; core++)
  24. {
  25. CoreContexts[core] = new KCoreContext(this, CoreManager);
  26. }
  27. }
  28. private void PreemptThreads()
  29. {
  30. _system.CriticalSection.Enter();
  31. PreemptThread(PreemptionPriorityCores012, 0);
  32. PreemptThread(PreemptionPriorityCores012, 1);
  33. PreemptThread(PreemptionPriorityCores012, 2);
  34. PreemptThread(PreemptionPriorityCore3, 3);
  35. _system.CriticalSection.Leave();
  36. }
  37. private void PreemptThread(int prio, int core)
  38. {
  39. IEnumerable<KThread> scheduledThreads = SchedulingData.ScheduledThreads(core);
  40. KThread selectedThread = scheduledThreads.FirstOrDefault(x => x.DynamicPriority == prio);
  41. // Yield priority queue.
  42. if (selectedThread != null)
  43. {
  44. SchedulingData.Reschedule(prio, core, selectedThread);
  45. }
  46. IEnumerable<KThread> SuitableCandidates()
  47. {
  48. foreach (KThread thread in SchedulingData.SuggestedThreads(core))
  49. {
  50. int srcCore = thread.CurrentCore;
  51. if (srcCore >= 0)
  52. {
  53. KThread highestPrioSrcCore = SchedulingData.ScheduledThreads(srcCore).FirstOrDefault();
  54. if (highestPrioSrcCore != null && highestPrioSrcCore.DynamicPriority < 2)
  55. {
  56. break;
  57. }
  58. if (highestPrioSrcCore == thread)
  59. {
  60. continue;
  61. }
  62. }
  63. // If the candidate was scheduled after the current thread, then it's not worth it.
  64. if (selectedThread == null || selectedThread.LastScheduledTime >= thread.LastScheduledTime)
  65. {
  66. yield return thread;
  67. }
  68. }
  69. }
  70. // Select candidate threads that could run on this core.
  71. // Only take into account threads that are not yet selected.
  72. KThread dst = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority == prio);
  73. if (dst != null)
  74. {
  75. SchedulingData.TransferToCore(prio, core, dst);
  76. selectedThread = dst;
  77. }
  78. // If the priority of the currently selected thread is lower than preemption priority,
  79. // then allow threads with lower priorities to be selected aswell.
  80. if (selectedThread != null && selectedThread.DynamicPriority > prio)
  81. {
  82. Func<KThread, bool> predicate = x => x.DynamicPriority >= selectedThread.DynamicPriority;
  83. dst = SuitableCandidates().FirstOrDefault(predicate);
  84. if (dst != null)
  85. {
  86. SchedulingData.TransferToCore(dst.DynamicPriority, core, dst);
  87. }
  88. }
  89. ThreadReselectionRequested = true;
  90. }
  91. public void SelectThreads()
  92. {
  93. ThreadReselectionRequested = false;
  94. for (int core = 0; core < CpuCoresCount; core++)
  95. {
  96. KThread thread = SchedulingData.ScheduledThreads(core).FirstOrDefault();
  97. CoreContexts[core].SelectThread(thread);
  98. }
  99. for (int core = 0; core < CpuCoresCount; core++)
  100. {
  101. // If the core is not idle (there's already a thread running on it),
  102. // then we don't need to attempt load balancing.
  103. if (SchedulingData.ScheduledThreads(core).Any())
  104. {
  105. continue;
  106. }
  107. int[] srcCoresHighestPrioThreads = new int[CpuCoresCount];
  108. int srcCoresHighestPrioThreadsCount = 0;
  109. KThread dst = null;
  110. // Select candidate threads that could run on this core.
  111. // Give preference to threads that are not yet selected.
  112. foreach (KThread thread in SchedulingData.SuggestedThreads(core))
  113. {
  114. if (thread.CurrentCore < 0 || thread != CoreContexts[thread.CurrentCore].SelectedThread)
  115. {
  116. dst = thread;
  117. break;
  118. }
  119. srcCoresHighestPrioThreads[srcCoresHighestPrioThreadsCount++] = thread.CurrentCore;
  120. }
  121. // Not yet selected candidate found.
  122. if (dst != null)
  123. {
  124. // Priorities < 2 are used for the kernel message dispatching
  125. // threads, we should skip load balancing entirely.
  126. if (dst.DynamicPriority >= 2)
  127. {
  128. SchedulingData.TransferToCore(dst.DynamicPriority, core, dst);
  129. CoreContexts[core].SelectThread(dst);
  130. }
  131. continue;
  132. }
  133. // All candidates are already selected, choose the best one
  134. // (the first one that doesn't make the source core idle if moved).
  135. for (int index = 0; index < srcCoresHighestPrioThreadsCount; index++)
  136. {
  137. int srcCore = srcCoresHighestPrioThreads[index];
  138. KThread src = SchedulingData.ScheduledThreads(srcCore).ElementAtOrDefault(1);
  139. if (src != null)
  140. {
  141. // Run the second thread on the queue on the source core,
  142. // move the first one to the current core.
  143. KThread origSelectedCoreSrc = CoreContexts[srcCore].SelectedThread;
  144. CoreContexts[srcCore].SelectThread(src);
  145. SchedulingData.TransferToCore(origSelectedCoreSrc.DynamicPriority, core, origSelectedCoreSrc);
  146. CoreContexts[core].SelectThread(origSelectedCoreSrc);
  147. }
  148. }
  149. }
  150. }
  151. public KThread GetCurrentThread()
  152. {
  153. lock (CoreContexts)
  154. {
  155. for (int core = 0; core < CpuCoresCount; core++)
  156. {
  157. if (CoreContexts[core].CurrentThread?.IsCurrentHostThread() ?? false)
  158. {
  159. return CoreContexts[core].CurrentThread;
  160. }
  161. }
  162. }
  163. return GetDummyThread();
  164. throw new InvalidOperationException("Current thread is not scheduled!");
  165. }
  166. private KThread _dummyThread;
  167. private KThread GetDummyThread()
  168. {
  169. if (_dummyThread != null)
  170. {
  171. return _dummyThread;
  172. }
  173. KProcess dummyProcess = new KProcess(_system);
  174. KThread dummyThread = new KThread(_system);
  175. dummyThread.Initialize(0, 0, 0, 44, 0, dummyProcess, ThreadType.Dummy);
  176. return _dummyThread = dummyThread;
  177. }
  178. public KProcess GetCurrentProcess()
  179. {
  180. return GetCurrentThread().Owner;
  181. }
  182. public void Dispose()
  183. {
  184. Dispose(true);
  185. }
  186. protected virtual void Dispose(bool disposing)
  187. {
  188. if (disposing)
  189. {
  190. _keepPreempting = false;
  191. }
  192. }
  193. }
  194. }