KProcessScheduler.cs 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. using Ryujinx.HLE.Logging;
  2. using System;
  3. using System.Collections.Concurrent;
  4. using System.Threading;
  5. namespace Ryujinx.HLE.OsHle.Handles
  6. {
  7. class KProcessScheduler : IDisposable
  8. {
  9. private ConcurrentDictionary<KThread, SchedulerThread> AllThreads;
  10. private ThreadQueue WaitingToRun;
  11. private KThread[] CoreThreads;
  12. private bool[] CoreReschedule;
  13. private object SchedLock;
  14. private Logger Log;
  15. public KProcessScheduler(Logger Log)
  16. {
  17. this.Log = Log;
  18. AllThreads = new ConcurrentDictionary<KThread, SchedulerThread>();
  19. WaitingToRun = new ThreadQueue();
  20. CoreThreads = new KThread[4];
  21. CoreReschedule = new bool[4];
  22. SchedLock = new object();
  23. }
  24. public void StartThread(KThread Thread)
  25. {
  26. lock (SchedLock)
  27. {
  28. SchedulerThread SchedThread = new SchedulerThread(Thread);
  29. if (!AllThreads.TryAdd(Thread, SchedThread))
  30. {
  31. return;
  32. }
  33. if (TryAddToCore(Thread))
  34. {
  35. Thread.Thread.Execute();
  36. PrintDbgThreadInfo(Thread, "running.");
  37. }
  38. else
  39. {
  40. WaitingToRun.Push(SchedThread);
  41. PrintDbgThreadInfo(Thread, "waiting to run.");
  42. }
  43. }
  44. }
  45. public void RemoveThread(KThread Thread)
  46. {
  47. PrintDbgThreadInfo(Thread, "exited.");
  48. lock (SchedLock)
  49. {
  50. if (AllThreads.TryRemove(Thread, out SchedulerThread SchedThread))
  51. {
  52. WaitingToRun.Remove(SchedThread);
  53. SchedThread.Dispose();
  54. }
  55. int ActualCore = Thread.ActualCore;
  56. SchedulerThread NewThread = WaitingToRun.Pop(ActualCore);
  57. if (NewThread == null)
  58. {
  59. Log.PrintDebug(LogClass.KernelScheduler, $"Nothing to run on core {ActualCore}!");
  60. CoreThreads[ActualCore] = null;
  61. return;
  62. }
  63. NewThread.Thread.ActualCore = ActualCore;
  64. RunThread(NewThread);
  65. }
  66. }
  67. public void SetThreadActivity(KThread Thread, bool Active)
  68. {
  69. if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
  70. {
  71. throw new InvalidOperationException();
  72. }
  73. SchedThread.IsActive = Active;
  74. if (Active)
  75. {
  76. SchedThread.WaitActivity.Set();
  77. }
  78. else
  79. {
  80. SchedThread.WaitActivity.Reset();
  81. }
  82. }
  83. public void EnterWait(KThread Thread, int TimeoutMs = Timeout.Infinite)
  84. {
  85. SchedulerThread SchedThread = AllThreads[Thread];
  86. Suspend(Thread);
  87. SchedThread.WaitSync.WaitOne(TimeoutMs);
  88. TryResumingExecution(SchedThread);
  89. }
  90. public void WakeUp(KThread Thread)
  91. {
  92. AllThreads[Thread].WaitSync.Set();
  93. }
  94. public void ChangeCore(KThread Thread, int IdealCore, int CoreMask)
  95. {
  96. lock (SchedLock)
  97. {
  98. if (IdealCore != -3)
  99. {
  100. Thread.IdealCore = IdealCore;
  101. }
  102. Thread.CoreMask = CoreMask;
  103. if (AllThreads.ContainsKey(Thread))
  104. {
  105. SetReschedule(Thread.ActualCore);
  106. SchedulerThread SchedThread = AllThreads[Thread];
  107. //Note: Aways if the thread is on the queue first, and try
  108. //adding to a new core later, to ensure that a thread that
  109. //is already running won't be added to another core.
  110. if (WaitingToRun.HasThread(SchedThread) && TryAddToCore(Thread))
  111. {
  112. WaitingToRun.Remove(SchedThread);
  113. RunThread(SchedThread);
  114. }
  115. }
  116. }
  117. }
  118. public void Suspend(KThread Thread)
  119. {
  120. lock (SchedLock)
  121. {
  122. PrintDbgThreadInfo(Thread, "suspended.");
  123. int ActualCore = Thread.ActualCore;
  124. CoreReschedule[ActualCore] = false;
  125. SchedulerThread SchedThread = WaitingToRun.Pop(ActualCore);
  126. if (SchedThread != null)
  127. {
  128. SchedThread.Thread.ActualCore = ActualCore;
  129. CoreThreads[ActualCore] = SchedThread.Thread;
  130. RunThread(SchedThread);
  131. }
  132. else
  133. {
  134. Log.PrintDebug(LogClass.KernelScheduler, $"Nothing to run on core {Thread.ActualCore}!");
  135. CoreThreads[ActualCore] = null;
  136. }
  137. }
  138. }
  139. public void SetReschedule(int Core)
  140. {
  141. lock (SchedLock)
  142. {
  143. CoreReschedule[Core] = true;
  144. }
  145. }
  146. public void Reschedule(KThread Thread)
  147. {
  148. bool NeedsReschedule;
  149. lock (SchedLock)
  150. {
  151. int ActualCore = Thread.ActualCore;
  152. NeedsReschedule = CoreReschedule[ActualCore];
  153. CoreReschedule[ActualCore] = false;
  154. }
  155. if (NeedsReschedule)
  156. {
  157. Yield(Thread, Thread.ActualPriority - 1);
  158. }
  159. }
  160. public void Yield(KThread Thread)
  161. {
  162. Yield(Thread, Thread.ActualPriority);
  163. }
  164. private void Yield(KThread Thread, int MinPriority)
  165. {
  166. PrintDbgThreadInfo(Thread, "yielded execution.");
  167. lock (SchedLock)
  168. {
  169. int ActualCore = Thread.ActualCore;
  170. SchedulerThread NewThread = WaitingToRun.Pop(ActualCore, MinPriority);
  171. if (NewThread != null)
  172. {
  173. NewThread.Thread.ActualCore = ActualCore;
  174. CoreThreads[ActualCore] = NewThread.Thread;
  175. RunThread(NewThread);
  176. }
  177. else
  178. {
  179. CoreThreads[ActualCore] = null;
  180. }
  181. }
  182. Resume(Thread);
  183. }
  184. public void Resume(KThread Thread)
  185. {
  186. TryResumingExecution(AllThreads[Thread]);
  187. }
  188. private void TryResumingExecution(SchedulerThread SchedThread)
  189. {
  190. KThread Thread = SchedThread.Thread;
  191. PrintDbgThreadInfo(Thread, "trying to resume...");
  192. SchedThread.WaitActivity.WaitOne();
  193. lock (SchedLock)
  194. {
  195. if (TryAddToCore(Thread))
  196. {
  197. PrintDbgThreadInfo(Thread, "resuming execution...");
  198. return;
  199. }
  200. WaitingToRun.Push(SchedThread);
  201. SetReschedule(Thread.ProcessorId);
  202. PrintDbgThreadInfo(Thread, "entering wait state...");
  203. }
  204. SchedThread.WaitSched.WaitOne();
  205. PrintDbgThreadInfo(Thread, "resuming execution...");
  206. }
  207. private void RunThread(SchedulerThread SchedThread)
  208. {
  209. if (!SchedThread.Thread.Thread.Execute())
  210. {
  211. PrintDbgThreadInfo(SchedThread.Thread, "waked.");
  212. SchedThread.WaitSched.Set();
  213. }
  214. else
  215. {
  216. PrintDbgThreadInfo(SchedThread.Thread, "running.");
  217. }
  218. }
  219. public void Resort(KThread Thread)
  220. {
  221. if (AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
  222. {
  223. WaitingToRun.Resort(SchedThread);
  224. }
  225. }
  226. private bool TryAddToCore(KThread Thread)
  227. {
  228. //First, try running it on Ideal Core.
  229. int IdealCore = Thread.IdealCore;
  230. if (IdealCore != -1 && CoreThreads[IdealCore] == null)
  231. {
  232. Thread.ActualCore = IdealCore;
  233. CoreThreads[IdealCore] = Thread;
  234. return true;
  235. }
  236. //If that fails, then try running on any core allowed by Core Mask.
  237. int CoreMask = Thread.CoreMask;
  238. for (int Core = 0; Core < CoreThreads.Length; Core++, CoreMask >>= 1)
  239. {
  240. if ((CoreMask & 1) != 0 && CoreThreads[Core] == null)
  241. {
  242. Thread.ActualCore = Core;
  243. CoreThreads[Core] = Thread;
  244. return true;
  245. }
  246. }
  247. return false;
  248. }
  249. private void PrintDbgThreadInfo(KThread Thread, string Message)
  250. {
  251. Log.PrintDebug(LogClass.KernelScheduler, "(" +
  252. "ThreadId = " + Thread.ThreadId + ", " +
  253. "CoreMask = 0x" + Thread.CoreMask.ToString("x1") + ", " +
  254. "ActualCore = " + Thread.ActualCore + ", " +
  255. "IdealCore = " + Thread.IdealCore + ", " +
  256. "ActualPriority = " + Thread.ActualPriority + ", " +
  257. "WantedPriority = " + Thread.WantedPriority + ") " + Message);
  258. }
  259. public void Dispose()
  260. {
  261. Dispose(true);
  262. }
  263. protected virtual void Dispose(bool Disposing)
  264. {
  265. if (Disposing)
  266. {
  267. foreach (SchedulerThread SchedThread in AllThreads.Values)
  268. {
  269. SchedThread.Dispose();
  270. }
  271. }
  272. }
  273. }
  274. }