KScheduler.cs 7.7 KB

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