KScheduler.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Threading;
  5. namespace Ryujinx.HLE.HOS.Kernel
  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. this.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. Thread PreemptionThread = new Thread(PreemptCurrentThread);
  28. KeepPreempting = true;
  29. PreemptionThread.Start();
  30. }
  31. private void PreemptThreads()
  32. {
  33. System.CriticalSectionLock.Lock();
  34. PreemptThread(PreemptionPriorityCores012, 0);
  35. PreemptThread(PreemptionPriorityCores012, 1);
  36. PreemptThread(PreemptionPriorityCores012, 2);
  37. PreemptThread(PreemptionPriorityCore3, 3);
  38. System.CriticalSectionLock.Unlock();
  39. }
  40. private void PreemptThread(int Prio, int Core)
  41. {
  42. IEnumerable<KThread> ScheduledThreads = SchedulingData.ScheduledThreads(Core);
  43. KThread SelectedThread = ScheduledThreads.FirstOrDefault(x => x.DynamicPriority == Prio);
  44. //Yield priority queue.
  45. if (SelectedThread != null)
  46. {
  47. SchedulingData.Reschedule(Prio, Core, SelectedThread);
  48. }
  49. IEnumerable<KThread> SuitableCandidates()
  50. {
  51. foreach (KThread Thread in SchedulingData.SuggestedThreads(Core))
  52. {
  53. int SrcCore = Thread.CurrentCore;
  54. if (SrcCore >= 0)
  55. {
  56. KThread HighestPrioSrcCore = SchedulingData.ScheduledThreads(SrcCore).FirstOrDefault();
  57. if (HighestPrioSrcCore != null && HighestPrioSrcCore.DynamicPriority < 2)
  58. {
  59. break;
  60. }
  61. if (HighestPrioSrcCore == Thread)
  62. {
  63. continue;
  64. }
  65. }
  66. //If the candidate was scheduled after the current thread, then it's not worth it.
  67. if (SelectedThread == null || SelectedThread.LastScheduledTicks >= Thread.LastScheduledTicks)
  68. {
  69. yield return Thread;
  70. }
  71. }
  72. }
  73. //Select candidate threads that could run on this core.
  74. //Only take into account threads that are not yet selected.
  75. KThread Dst = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority == Prio);
  76. if (Dst != null)
  77. {
  78. SchedulingData.TransferToCore(Prio, Core, Dst);
  79. SelectedThread = Dst;
  80. }
  81. //If the priority of the currently selected thread is lower than preemption priority,
  82. //then allow threads with lower priorities to be selected aswell.
  83. if (SelectedThread != null && SelectedThread.DynamicPriority > Prio)
  84. {
  85. Func<KThread, bool> Predicate = x => x.DynamicPriority >= SelectedThread.DynamicPriority;
  86. Dst = SuitableCandidates().FirstOrDefault(Predicate);
  87. if (Dst != null)
  88. {
  89. SchedulingData.TransferToCore(Dst.DynamicPriority, Core, Dst);
  90. }
  91. }
  92. ThreadReselectionRequested = true;
  93. }
  94. public void SelectThreads()
  95. {
  96. ThreadReselectionRequested = false;
  97. for (int Core = 0; Core < CpuCoresCount; Core++)
  98. {
  99. KThread Thread = SchedulingData.ScheduledThreads(Core).FirstOrDefault();
  100. CoreContexts[Core].SelectThread(Thread);
  101. }
  102. for (int Core = 0; Core < CpuCoresCount; Core++)
  103. {
  104. //If the core is not idle (there's already a thread running on it),
  105. //then we don't need to attempt load balancing.
  106. if (SchedulingData.ScheduledThreads(Core).Any())
  107. {
  108. continue;
  109. }
  110. int[] SrcCoresHighestPrioThreads = new int[CpuCoresCount];
  111. int SrcCoresHighestPrioThreadsCount = 0;
  112. KThread Dst = null;
  113. //Select candidate threads that could run on this core.
  114. //Give preference to threads that are not yet selected.
  115. foreach (KThread Thread in SchedulingData.SuggestedThreads(Core))
  116. {
  117. if (Thread.CurrentCore < 0 || Thread != CoreContexts[Thread.CurrentCore].SelectedThread)
  118. {
  119. Dst = Thread;
  120. break;
  121. }
  122. SrcCoresHighestPrioThreads[SrcCoresHighestPrioThreadsCount++] = Thread.CurrentCore;
  123. }
  124. //Not yet selected candidate found.
  125. if (Dst != null)
  126. {
  127. //Priorities < 2 are used for the kernel message dispatching
  128. //threads, we should skip load balancing entirely.
  129. if (Dst.DynamicPriority >= 2)
  130. {
  131. SchedulingData.TransferToCore(Dst.DynamicPriority, Core, Dst);
  132. CoreContexts[Core].SelectThread(Dst);
  133. }
  134. continue;
  135. }
  136. //All candiates are already selected, choose the best one
  137. //(the first one that doesn't make the source core idle if moved).
  138. for (int Index = 0; Index < SrcCoresHighestPrioThreadsCount; Index++)
  139. {
  140. int SrcCore = SrcCoresHighestPrioThreads[Index];
  141. KThread Src = SchedulingData.ScheduledThreads(SrcCore).ElementAtOrDefault(1);
  142. if (Src != null)
  143. {
  144. //Run the second thread on the queue on the source core,
  145. //move the first one to the current core.
  146. KThread OrigSelectedCoreSrc = CoreContexts[SrcCore].SelectedThread;
  147. CoreContexts[SrcCore].SelectThread(Src);
  148. SchedulingData.TransferToCore(OrigSelectedCoreSrc.DynamicPriority, Core, OrigSelectedCoreSrc);
  149. CoreContexts[Core].SelectThread(OrigSelectedCoreSrc);
  150. }
  151. }
  152. }
  153. }
  154. public KThread GetCurrentThread()
  155. {
  156. lock (CoreContexts)
  157. {
  158. for (int Core = 0; Core < CpuCoresCount; Core++)
  159. {
  160. if (CoreContexts[Core].CurrentThread?.Context.IsCurrentThread() ?? false)
  161. {
  162. return CoreContexts[Core].CurrentThread;
  163. }
  164. }
  165. }
  166. throw new InvalidOperationException("Current thread is not scheduled!");
  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. }