KProcessScheduler.cs 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Threading;
  4. namespace Ryujinx.OsHle.Handles
  5. {
  6. class KProcessScheduler : IDisposable
  7. {
  8. private enum ThreadState
  9. {
  10. WaitingToRun,
  11. WaitingSignal,
  12. Running
  13. }
  14. private class SchedulerThread : IDisposable
  15. {
  16. public bool Signaled { get; set; }
  17. public ThreadState State { get; set; }
  18. public HThread Thread { get; private set; }
  19. public AutoResetEvent WaitEvent { get; private set; }
  20. public SchedulerThread(HThread Thread)
  21. {
  22. this.Thread = Thread;
  23. WaitEvent = new AutoResetEvent(false);
  24. }
  25. public void Dispose()
  26. {
  27. Dispose(true);
  28. }
  29. protected virtual void Dispose(bool Disposing)
  30. {
  31. if (Disposing)
  32. {
  33. WaitEvent.Dispose();
  34. }
  35. }
  36. }
  37. private Dictionary<HThread, SchedulerThread> AllThreads;
  38. private Queue<SchedulerThread>[] WaitingThreads;
  39. private HashSet<int> ActiveProcessors;
  40. private object SchedLock;
  41. public KProcessScheduler()
  42. {
  43. AllThreads = new Dictionary<HThread, SchedulerThread>();
  44. WaitingThreads = new Queue<SchedulerThread>[4];
  45. for (int Index = 0; Index < WaitingThreads.Length; Index++)
  46. {
  47. WaitingThreads[Index] = new Queue<SchedulerThread>();
  48. }
  49. ActiveProcessors = new HashSet<int>();
  50. SchedLock = new object();
  51. }
  52. public void StartThread(HThread Thread)
  53. {
  54. lock (SchedLock)
  55. {
  56. if (AllThreads.ContainsKey(Thread))
  57. {
  58. return;
  59. }
  60. SchedulerThread SchedThread = new SchedulerThread(Thread);
  61. AllThreads.Add(Thread, SchedThread);
  62. if (!ActiveProcessors.Contains(Thread.ProcessorId))
  63. {
  64. ActiveProcessors.Add(Thread.ProcessorId);
  65. Thread.Thread.Execute();
  66. SetThreadAsRunning(SchedThread);
  67. SchedThread.State = ThreadState.Running;
  68. }
  69. else
  70. {
  71. InsertSorted(SchedThread);
  72. SchedThread.State = ThreadState.WaitingToRun;
  73. Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} waiting to run.");
  74. }
  75. }
  76. }
  77. public void WaitForSignal(HThread Thread, int TimeoutMs)
  78. {
  79. Logging.Debug($"{GetDbgThreadInfo(Thread)} entering signal wait state with timeout.");
  80. PutThreadToWait(Thread, ThreadState.WaitingSignal, TimeoutMs);
  81. }
  82. public void WaitForSignal(HThread Thread)
  83. {
  84. Logging.Debug($"{GetDbgThreadInfo(Thread)} entering signal wait state.");
  85. PutThreadToWait(Thread, ThreadState.WaitingSignal);
  86. }
  87. public void Yield(HThread Thread)
  88. {
  89. Logging.Debug($"{GetDbgThreadInfo(Thread)} yielded execution.");
  90. if (WaitingThreads[Thread.ProcessorId].Count == 0)
  91. {
  92. Logging.Debug($"{GetDbgThreadInfo(Thread)} resumed because theres nothing to run.");
  93. return;
  94. }
  95. PutThreadToWait(Thread, ThreadState.WaitingToRun);
  96. }
  97. private void PutThreadToWait(HThread Thread, ThreadState State, int TimeoutMs = -1)
  98. {
  99. SchedulerThread SchedThread;
  100. lock (SchedLock)
  101. {
  102. if (!AllThreads.TryGetValue(Thread, out SchedThread))
  103. {
  104. return;
  105. }
  106. if (SchedThread.Signaled && SchedThread.State == ThreadState.WaitingSignal)
  107. {
  108. SchedThread.Signaled = false;
  109. return;
  110. }
  111. ActiveProcessors.Remove(Thread.ProcessorId);
  112. SchedThread.State = State;
  113. TryRunningWaitingThead(SchedThread.Thread.ProcessorId);
  114. if (State == ThreadState.WaitingSignal)
  115. {
  116. InsertSorted(SchedThread);
  117. }
  118. else
  119. {
  120. InsertAtEnd(SchedThread);
  121. }
  122. }
  123. if (TimeoutMs >= 0)
  124. {
  125. Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} waiting with timeout of {TimeoutMs}ms.");
  126. SchedThread.WaitEvent.WaitOne(TimeoutMs);
  127. }
  128. else
  129. {
  130. Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} waiting indefinitely.");
  131. SchedThread.WaitEvent.WaitOne();
  132. }
  133. while (true)
  134. {
  135. lock (SchedLock)
  136. {
  137. Logging.Debug($"Trying to run {GetDbgThreadInfo(SchedThread.Thread)}.");
  138. if (!ActiveProcessors.Contains(SchedThread.Thread.ProcessorId))
  139. {
  140. SetThreadAsRunning(SchedThread);
  141. break;
  142. }
  143. else
  144. {
  145. SchedThread.State = ThreadState.WaitingToRun;
  146. Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} waiting to run.");
  147. }
  148. }
  149. SchedThread.WaitEvent.WaitOne();
  150. }
  151. }
  152. public void Signal(params HThread[] Threads)
  153. {
  154. lock (SchedLock)
  155. {
  156. HashSet<int> SignaledProcessorIds = new HashSet<int>();
  157. foreach (HThread Thread in Threads)
  158. {
  159. Logging.Debug($"{GetDbgThreadInfo(Thread)} signaled.");
  160. if (AllThreads.TryGetValue(Thread, out SchedulerThread SchedThread))
  161. {
  162. if (SchedThread.State == ThreadState.WaitingSignal)
  163. {
  164. SchedThread.State = ThreadState.WaitingToRun;
  165. SignaledProcessorIds.Add(Thread.ProcessorId);
  166. }
  167. SchedThread.Signaled = true;
  168. }
  169. }
  170. foreach (int ProcessorId in SignaledProcessorIds)
  171. {
  172. TryRunningWaitingThead(ProcessorId);
  173. }
  174. }
  175. }
  176. private void TryRunningWaitingThead(int ProcessorId)
  177. {
  178. Logging.Debug($"TryRunningWaitingThead core {ProcessorId}.");
  179. lock (SchedLock)
  180. {
  181. if (!ActiveProcessors.Contains(ProcessorId) && WaitingThreads[ProcessorId].Count > 0)
  182. {
  183. SchedulerThread SchedThread = WaitingThreads[ProcessorId].Dequeue();
  184. Logging.Debug($"Now trying to run {GetDbgThreadInfo(SchedThread.Thread)}.");
  185. if (!SchedThread.Thread.Thread.Execute())
  186. {
  187. SchedThread.WaitEvent.Set();
  188. }
  189. else
  190. {
  191. SetThreadAsRunning(SchedThread);
  192. }
  193. }
  194. else
  195. {
  196. Logging.Debug($"Processor id {ProcessorId} already being used or no waiting threads.");
  197. }
  198. }
  199. }
  200. private void SetThreadAsRunning(SchedulerThread SchedThread)
  201. {
  202. ActiveProcessors.Add(SchedThread.Thread.ProcessorId);
  203. SchedThread.State = ThreadState.Running;
  204. SchedThread.Signaled = false;
  205. Logging.Debug($"{GetDbgThreadInfo(SchedThread.Thread)} running.");
  206. }
  207. private void InsertSorted(SchedulerThread SchedThread)
  208. {
  209. HThread Thread = SchedThread.Thread;
  210. Queue<SchedulerThread> CoreQueue = WaitingThreads[Thread.ProcessorId];
  211. Queue<SchedulerThread> TempQueue = new Queue<SchedulerThread>(CoreQueue.Count);
  212. while (CoreQueue.Count > 0)
  213. {
  214. if (CoreQueue.Peek().Thread.Priority >= Thread.Priority)
  215. {
  216. break;
  217. }
  218. TempQueue.Enqueue(CoreQueue.Dequeue());
  219. }
  220. CoreQueue.Enqueue(SchedThread);
  221. while (CoreQueue.Count > 0)
  222. {
  223. TempQueue.Enqueue(CoreQueue.Dequeue());
  224. }
  225. while (TempQueue.Count > 0)
  226. {
  227. CoreQueue.Enqueue(TempQueue.Dequeue());
  228. }
  229. }
  230. private void InsertAtEnd(SchedulerThread SchedThread)
  231. {
  232. WaitingThreads[SchedThread.Thread.ProcessorId].Enqueue(SchedThread);
  233. }
  234. private string GetDbgThreadInfo(HThread Thread)
  235. {
  236. return $"Thread {Thread.ThreadId} (core {Thread.ProcessorId}) prio {Thread.Priority}";
  237. }
  238. public void Dispose()
  239. {
  240. Dispose(true);
  241. }
  242. protected virtual void Dispose(bool Disposing)
  243. {
  244. if (Disposing)
  245. {
  246. foreach (Queue<SchedulerThread> SchedThreads in WaitingThreads)
  247. {
  248. foreach (SchedulerThread SchedThread in SchedThreads)
  249. {
  250. SchedThread.Dispose();
  251. }
  252. SchedThreads.Clear();
  253. }
  254. }
  255. }
  256. }
  257. }