KProcessScheduler.cs 11 KB

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