HleScheduler.cs 4.5 KB

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