KProcessScheduler.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. using Ryujinx.Core.Logging;
  2. using System;
  3. using System.Collections.Concurrent;
  4. using System.Collections.Generic;
  5. using System.Threading;
  6. namespace Ryujinx.Core.OsHle.Handles
  7. {
  8. class KProcessScheduler : IDisposable
  9. {
  10. private const int LowestPriority = 0x40;
  11. private class SchedulerThread : IDisposable
  12. {
  13. public KThread Thread { get; private set; }
  14. public ManualResetEvent SyncWaitEvent { get; private set; }
  15. public AutoResetEvent SchedWaitEvent { get; private set; }
  16. public bool Active { get; set; }
  17. public int SyncTimeout { get; set; }
  18. public SchedulerThread(KThread Thread)
  19. {
  20. this.Thread = Thread;
  21. SyncWaitEvent = new ManualResetEvent(true);
  22. SchedWaitEvent = new AutoResetEvent(false);
  23. Active = true;
  24. SyncTimeout = 0;
  25. }
  26. public void Dispose()
  27. {
  28. Dispose(true);
  29. }
  30. protected virtual void Dispose(bool Disposing)
  31. {
  32. if (Disposing)
  33. {
  34. SyncWaitEvent.Dispose();
  35. SchedWaitEvent.Dispose();
  36. }
  37. }
  38. }
  39. private class ThreadQueue
  40. {
  41. private List<SchedulerThread> Threads;
  42. public ThreadQueue()
  43. {
  44. Threads = new List<SchedulerThread>();
  45. }
  46. public void Push(SchedulerThread Thread)
  47. {
  48. lock (Threads)
  49. {
  50. Threads.Add(Thread);
  51. }
  52. }
  53. public SchedulerThread Pop(int MinPriority = LowestPriority)
  54. {
  55. lock (Threads)
  56. {
  57. SchedulerThread SchedThread;
  58. int HighestPriority = MinPriority;
  59. int HighestPrioIndex = -1;
  60. for (int Index = 0; Index < Threads.Count; Index++)
  61. {
  62. SchedThread = Threads[Index];
  63. if (HighestPriority > SchedThread.Thread.ActualPriority)
  64. {
  65. HighestPriority = SchedThread.Thread.ActualPriority;
  66. HighestPrioIndex = Index;
  67. }
  68. }
  69. if (HighestPrioIndex == -1)
  70. {
  71. return null;
  72. }
  73. SchedThread = Threads[HighestPrioIndex];
  74. Threads.RemoveAt(HighestPrioIndex);
  75. return SchedThread;
  76. }
  77. }
  78. public bool HasThread(SchedulerThread SchedThread)
  79. {
  80. lock (Threads)
  81. {
  82. return Threads.Contains(SchedThread);
  83. }
  84. }
  85. public bool Remove(SchedulerThread SchedThread)
  86. {
  87. lock (Threads)
  88. {
  89. return Threads.Remove(SchedThread);
  90. }
  91. }
  92. }
  93. private ConcurrentDictionary<KThread, SchedulerThread> AllThreads;
  94. private ThreadQueue[] WaitingToRun;
  95. private HashSet<int> ActiveProcessors;
  96. private object SchedLock;
  97. private Logger Log;
  98. public KProcessScheduler(Logger Log)
  99. {
  100. this.Log = Log;
  101. AllThreads = new ConcurrentDictionary<KThread, SchedulerThread>();
  102. WaitingToRun = new ThreadQueue[4];
  103. for (int Index = 0; Index < 4; Index++)
  104. {
  105. WaitingToRun[Index] = new ThreadQueue();
  106. }
  107. ActiveProcessors = new HashSet<int>();
  108. SchedLock = new object();
  109. }
  110. public void StartThread(KThread Thread)
  111. {
  112. lock (SchedLock)
  113. {
  114. SchedulerThread SchedThread = new SchedulerThread(Thread);
  115. if (!AllThreads.TryAdd(Thread, SchedThread))
  116. {
  117. return;
  118. }
  119. if (ActiveProcessors.Add(Thread.ProcessorId))
  120. {
  121. Thread.Thread.Execute();
  122. PrintDbgThreadInfo(Thread, "running.");
  123. }
  124. else
  125. {
  126. WaitingToRun[Thread.ProcessorId].Push(SchedThread);
  127. PrintDbgThreadInfo(Thread, "waiting to run.");
  128. }
  129. }
  130. }
  131. public void RemoveThread(KThread Thread)
  132. {
  133. PrintDbgThreadInfo(Thread, "exited.");
  134. lock (SchedLock)
  135. {
  136. if (AllThreads.TryRemove(Thread, out SchedulerThread SchedThread))
  137. {
  138. WaitingToRun[Thread.ProcessorId].Remove(SchedThread);
  139. SchedThread.Dispose();
  140. }
  141. SchedulerThread NewThread = WaitingToRun[Thread.ProcessorId].Pop();
  142. if (NewThread == null)
  143. {
  144. Log.PrintDebug(LogClass.KernelScheduler, $"Nothing to run on core {Thread.ProcessorId}!");
  145. ActiveProcessors.Remove(Thread.ProcessorId);
  146. return;
  147. }
  148. RunThread(NewThread);
  149. }
  150. }
  151. public void SetThreadActivity(KThread Thread, bool Active)
  152. {
  153. if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
  154. {
  155. throw new InvalidOperationException();
  156. }
  157. SchedThread.Active = Active;
  158. UpdateSyncWaitEvent(SchedThread);
  159. WaitIfNeeded(SchedThread);
  160. }
  161. public bool EnterWait(KThread Thread, int Timeout = -1)
  162. {
  163. if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
  164. {
  165. throw new InvalidOperationException();
  166. }
  167. SchedThread.SyncTimeout = Timeout;
  168. UpdateSyncWaitEvent(SchedThread);
  169. return WaitIfNeeded(SchedThread);
  170. }
  171. public void WakeUp(KThread Thread)
  172. {
  173. if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
  174. {
  175. throw new InvalidOperationException();
  176. }
  177. SchedThread.SyncTimeout = 0;
  178. UpdateSyncWaitEvent(SchedThread);
  179. WaitIfNeeded(SchedThread);
  180. }
  181. private void UpdateSyncWaitEvent(SchedulerThread SchedThread)
  182. {
  183. if (SchedThread.Active && SchedThread.SyncTimeout == 0)
  184. {
  185. SchedThread.SyncWaitEvent.Set();
  186. }
  187. else
  188. {
  189. SchedThread.SyncWaitEvent.Reset();
  190. }
  191. }
  192. private bool WaitIfNeeded(SchedulerThread SchedThread)
  193. {
  194. KThread Thread = SchedThread.Thread;
  195. if (!IsActive(SchedThread) && Thread.Thread.IsCurrentThread())
  196. {
  197. Suspend(Thread.ProcessorId);
  198. return Resume(Thread);
  199. }
  200. else
  201. {
  202. return false;
  203. }
  204. }
  205. public void Suspend(int ProcessorId)
  206. {
  207. lock (SchedLock)
  208. {
  209. SchedulerThread SchedThread = WaitingToRun[ProcessorId].Pop();
  210. if (SchedThread != null)
  211. {
  212. RunThread(SchedThread);
  213. }
  214. else
  215. {
  216. Log.PrintDebug(LogClass.KernelScheduler, $"Nothing to run on core {ProcessorId}!");
  217. ActiveProcessors.Remove(ProcessorId);
  218. }
  219. }
  220. }
  221. public void Yield(KThread Thread)
  222. {
  223. PrintDbgThreadInfo(Thread, "yielded execution.");
  224. lock (SchedLock)
  225. {
  226. SchedulerThread SchedThread = WaitingToRun[Thread.ProcessorId].Pop(Thread.ActualPriority);
  227. if (IsActive(Thread) && SchedThread == null)
  228. {
  229. PrintDbgThreadInfo(Thread, "resumed because theres nothing better to run.");
  230. return;
  231. }
  232. if (SchedThread != null)
  233. {
  234. RunThread(SchedThread);
  235. }
  236. }
  237. Resume(Thread);
  238. }
  239. public bool Resume(KThread Thread)
  240. {
  241. if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
  242. {
  243. throw new InvalidOperationException();
  244. }
  245. return TryResumingExecution(SchedThread);
  246. }
  247. private bool TryResumingExecution(SchedulerThread SchedThread)
  248. {
  249. KThread Thread = SchedThread.Thread;
  250. if (!SchedThread.Active || SchedThread.SyncTimeout != 0)
  251. {
  252. PrintDbgThreadInfo(Thread, "entering inactive wait state...");
  253. }
  254. bool Result = false;
  255. if (SchedThread.SyncTimeout != 0)
  256. {
  257. Result = SchedThread.SyncWaitEvent.WaitOne(SchedThread.SyncTimeout);
  258. SchedThread.SyncTimeout = 0;
  259. }
  260. lock (SchedLock)
  261. {
  262. if (ActiveProcessors.Add(Thread.ProcessorId))
  263. {
  264. PrintDbgThreadInfo(Thread, "resuming execution...");
  265. return Result;
  266. }
  267. WaitingToRun[Thread.ProcessorId].Push(SchedThread);
  268. PrintDbgThreadInfo(Thread, "entering wait state...");
  269. }
  270. SchedThread.SchedWaitEvent.WaitOne();
  271. PrintDbgThreadInfo(Thread, "resuming execution...");
  272. return Result;
  273. }
  274. private void RunThread(SchedulerThread SchedThread)
  275. {
  276. if (!SchedThread.Thread.Thread.Execute())
  277. {
  278. SchedThread.SchedWaitEvent.Set();
  279. }
  280. else
  281. {
  282. PrintDbgThreadInfo(SchedThread.Thread, "running.");
  283. }
  284. }
  285. private bool IsActive(KThread Thread)
  286. {
  287. if (!AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
  288. {
  289. throw new InvalidOperationException();
  290. }
  291. return IsActive(SchedThread);
  292. }
  293. private bool IsActive(SchedulerThread SchedThread)
  294. {
  295. return SchedThread.Active && SchedThread.SyncTimeout == 0;
  296. }
  297. private void PrintDbgThreadInfo(KThread Thread, string Message)
  298. {
  299. Log.PrintDebug(LogClass.KernelScheduler, "(" +
  300. "ThreadId: " + Thread.ThreadId + ", " +
  301. "ProcessorId: " + Thread.ProcessorId + ", " +
  302. "ActualPriority: " + Thread.ActualPriority + ", " +
  303. "WantedPriority: " + Thread.WantedPriority + ") " + Message);
  304. }
  305. public void Dispose()
  306. {
  307. Dispose(true);
  308. }
  309. protected virtual void Dispose(bool Disposing)
  310. {
  311. if (Disposing)
  312. {
  313. foreach (SchedulerThread SchedThread in AllThreads.Values)
  314. {
  315. SchedThread.Dispose();
  316. }
  317. }
  318. }
  319. }
  320. }