HleScheduler.cs 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. using System;
  2. using System.Threading;
  3. namespace Ryujinx.HLE.HOS.Kernel
  4. {
  5. partial class KScheduler
  6. {
  7. private const int RoundRobinTimeQuantumMs = 10;
  8. private int CurrentCore;
  9. public bool MultiCoreScheduling { get; set; }
  10. private HleCoreManager CoreManager;
  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 < KScheduler.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.GetThread(Thread.CurrentThread).Reset();
  40. }
  41. else if (SelectedCount == 1)
  42. {
  43. CoreManager.GetThread(Thread.CurrentThread).Set();
  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.GetThread(CurrentThread.Context.Work).Reset();
  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.GetThread(CoreContext.CurrentThread.Context.Work).Set();
  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.GetThread(Thread.CurrentThread).WaitOne();
  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 StopThread(KThread Thread)
  107. {
  108. Thread.Context.StopExecution();
  109. CoreManager.GetThread(Thread.Context.Work).Set();
  110. }
  111. public void RemoveThread(KThread Thread)
  112. {
  113. CoreManager.RemoveThread(Thread.Context.Work);
  114. }
  115. }
  116. }