KProcessScheduler.cs 10 KB

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