HleScheduler.cs 4.7 KB

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