HleScheduler.cs 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. using System;
  2. using System.Threading;
  3. namespace Ryujinx.HLE.HOS.Kernel.Threading
  4. {
  5. partial class KScheduler
  6. {
  7. private const int RoundRobinTimeQuantumMs = 10;
  8. private int _currentCore;
  9. public bool MultiCoreScheduling { get; set; }
  10. public HleCoreManager CoreManager { get; private set; }
  11. private bool _keepPreempting;
  12. public void StartAutoPreemptionThread()
  13. {
  14. Thread preemptionThread = new Thread(PreemptCurrentThread);
  15. _keepPreempting = true;
  16. preemptionThread.Start();
  17. }
  18. public void ContextSwitch()
  19. {
  20. lock (CoreContexts)
  21. {
  22. if (MultiCoreScheduling)
  23. {
  24. int selectedCount = 0;
  25. for (int core = 0; core < CpuCoresCount; core++)
  26. {
  27. KCoreContext coreContext = CoreContexts[core];
  28. if (coreContext.ContextSwitchNeeded && (coreContext.CurrentThread?.Context.IsCurrentThread() ?? false))
  29. {
  30. coreContext.ContextSwitch();
  31. }
  32. if (coreContext.CurrentThread?.Context.IsCurrentThread() ?? false)
  33. {
  34. selectedCount++;
  35. }
  36. }
  37. if (selectedCount == 0)
  38. {
  39. CoreManager.Reset(Thread.CurrentThread);
  40. }
  41. else if (selectedCount == 1)
  42. {
  43. CoreManager.Set(Thread.CurrentThread);
  44. }
  45. else
  46. {
  47. throw new InvalidOperationException("Thread scheduled in more than one core!");
  48. }
  49. }
  50. else
  51. {
  52. KThread currentThread = CoreContexts[_currentCore].CurrentThread;
  53. bool hasThreadExecuting = currentThread != null;
  54. if (hasThreadExecuting)
  55. {
  56. //If this is not the thread that is currently executing, we need
  57. //to request an interrupt to allow safely starting another thread.
  58. if (!currentThread.Context.IsCurrentThread())
  59. {
  60. currentThread.Context.RequestInterrupt();
  61. return;
  62. }
  63. CoreManager.Reset(currentThread.Context.Work);
  64. }
  65. //Advance current core and try picking a thread,
  66. //keep advancing if it is null.
  67. for (int core = 0; core < 4; core++)
  68. {
  69. _currentCore = (_currentCore + 1) % CpuCoresCount;
  70. KCoreContext coreContext = CoreContexts[_currentCore];
  71. coreContext.UpdateCurrentThread();
  72. if (coreContext.CurrentThread != null)
  73. {
  74. coreContext.CurrentThread.ClearExclusive();
  75. CoreManager.Set(coreContext.CurrentThread.Context.Work);
  76. coreContext.CurrentThread.Context.Execute();
  77. break;
  78. }
  79. }
  80. //If nothing was running before, then we are on a "external"
  81. //HLE thread, we don't need to wait.
  82. if (!hasThreadExecuting)
  83. {
  84. return;
  85. }
  86. }
  87. }
  88. CoreManager.Wait(Thread.CurrentThread);
  89. }
  90. private void PreemptCurrentThread()
  91. {
  92. //Preempts current thread every 10 milliseconds on a round-robin fashion,
  93. //when multi core scheduling is disabled, to try ensuring that all threads
  94. //gets a chance to run.
  95. while (_keepPreempting)
  96. {
  97. lock (CoreContexts)
  98. {
  99. KThread currentThread = CoreContexts[_currentCore].CurrentThread;
  100. currentThread?.Context.RequestInterrupt();
  101. }
  102. PreemptThreads();
  103. Thread.Sleep(RoundRobinTimeQuantumMs);
  104. }
  105. }
  106. public void ExitThread(KThread thread)
  107. {
  108. thread.Context.StopExecution();
  109. CoreManager.Exit(thread.Context.Work);
  110. }
  111. public void RemoveThread(KThread thread)
  112. {
  113. CoreManager.RemoveThread(thread.Context.Work);
  114. }
  115. }
  116. }