HleScheduler.cs 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  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?.IsCurrentHostThread() ?? false))
  29. {
  30. coreContext.ContextSwitch();
  31. }
  32. if (coreContext.CurrentThread?.IsCurrentHostThread() ?? 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.IsCurrentHostThread())
  59. {
  60. currentThread.Context.RequestInterrupt();
  61. return;
  62. }
  63. CoreManager.Reset(currentThread.HostThread);
  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. CoreManager.Set(coreContext.CurrentThread.HostThread);
  75. coreContext.CurrentThread.Execute();
  76. break;
  77. }
  78. }
  79. // If nothing was running before, then we are on a "external"
  80. // HLE thread, we don't need to wait.
  81. if (!hasThreadExecuting)
  82. {
  83. return;
  84. }
  85. }
  86. }
  87. CoreManager.Wait(Thread.CurrentThread);
  88. }
  89. private void PreemptCurrentThread()
  90. {
  91. // Preempts current thread every 10 milliseconds on a round-robin fashion,
  92. // when multi core scheduling is disabled, to try ensuring that all threads
  93. // gets a chance to run.
  94. while (_keepPreempting)
  95. {
  96. lock (CoreContexts)
  97. {
  98. KThread currentThread = CoreContexts[_currentCore].CurrentThread;
  99. currentThread?.Context.RequestInterrupt();
  100. }
  101. PreemptThreads();
  102. Thread.Sleep(RoundRobinTimeQuantumMs);
  103. }
  104. }
  105. public void ExitThread(KThread thread)
  106. {
  107. thread.Context.Running = false;
  108. CoreManager.Exit(thread.HostThread);
  109. }
  110. public void RemoveThread(KThread thread)
  111. {
  112. CoreManager.RemoveThread(thread.HostThread);
  113. }
  114. }
  115. }