KScheduler.cs 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  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. this.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.CriticalSectionLock.Lock();
  30. PreemptThread(PreemptionPriorityCores012, 0);
  31. PreemptThread(PreemptionPriorityCores012, 1);
  32. PreemptThread(PreemptionPriorityCores012, 2);
  33. PreemptThread(PreemptionPriorityCore3, 3);
  34. System.CriticalSectionLock.Unlock();
  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.LastScheduledTicks >= Thread.LastScheduledTicks)
  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 void Dispose()
  165. {
  166. Dispose(true);
  167. }
  168. protected virtual void Dispose(bool Disposing)
  169. {
  170. if (Disposing)
  171. {
  172. KeepPreempting = false;
  173. }
  174. }
  175. }
  176. }